/////////////////////////////////////////////////////////////////////////
// $Id: usb_xhci.h 14158 2021-02-20 19:58:39Z vruppert $
/////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2010-2017  Benjamin D Lunt (fys [at] fysnet [dot] net)
//                2011-2021  The Bochs Project
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
/////////////////////////////////////////////////////////////////////////

#ifndef BX_IODEV_USB_XHCI_H
#define BX_IODEV_USB_XHCI_H

#if BX_USE_USB_XHCI_SMF
#  define BX_XHCI_THIS theUSB_XHCI->
#  define BX_XHCI_THIS_PTR theUSB_XHCI
#else
#  define BX_XHCI_THIS this->
#  define BX_XHCI_THIS_PTR this
#endif

// If in 64bit mode, print 64bits, else only print 32 bit addresses
#if BX_PHY_ADDRESS_LONG
  #define FORMATADDRESS   FMT_ADDRX64
#else
  #define FORMATADDRESS   "%08X"
#endif

/************************************************************************************************
 * Actual configuration of the card
 */
#define IO_SPACE_SIZE     8192

#define OPS_REGS_OFFSET   0x20
// Change this to 0.95, 0.96, 1.00, 1.10, according to the desired effects (LINK chain bit, etc)
//   uPD720202 is 1.00
#define VERSION_MAJOR     0x01
#define VERSION_MINOR     0x00

// HCSPARAMS1
#define MAX_SLOTS           32   // (1 based)
#define INTERRUPTERS         8   //
#define USB_XHCI_PORTS       4   // Port Registers, each supporting USB3 or USB2 (0x08 = uPD720201, 0x04 = uPD720202)

#if (USB_XHCI_PORTS != 4)
  #error "USB_XHCI_PORTS must equal 4"
#endif

// HCSPARAMS2
#define ISO_SECH_THRESHOLD   1
#define MAX_SEG_TBL_SZ_EXP   1
#define SCATCH_PAD_RESTORE   1  // 1 = uses system memory and must be maintained.  0 = uses controller's internal memory
#define MAX_SCRATCH_PADS     4  // 0 to 1023

// HCSPARAMS3
#define U1_DEVICE_EXIT_LAT   0
#define U2_DEVICE_EXIT_LAT   0

// HCCPARAMS1
#define ADDR_CAP_64              1
#define BW_NEGOTIATION           1
#define CONTEXT_SIZE            64  // Size of the CONTEXT blocks (32 or 64)
#define PORT_POWER_CTRL          1
#define PORT_INDICATORS          0
#define LIGHT_HC_RESET           0  // Do we support the Light HC Reset function
#define LAT_TOL_MSGING_CAP       1  // Latency Tolerance Messaging Capability (v1.00+)
#define NO_SSD_SUPPORT           1  // No Secondary SID Support (v1.00+)
#define PARSE_ALL_EVENT          1  // version 0.96 and below only (MUST BE 1 in v1.00+)
#define SEC_DOMAIN_BAND          1  // version 0.96 and below only (MUST BE 1 in v1.00+)
#define STOPPED_EDTLA            0
#define CONT_FRAME_ID            0
#define MAX_PSA_SIZE          0x05
#define EXT_CAPS_OFFSET      0x500
  #define EXT_CAPS_SIZE        144

// HCCPARAMS2 (v1.10+)
#if ((VERSION_MAJOR == 1) && (VERSION_MINOR >= 0x10))
  #define U3_ENTRY_CAP             0
  #define CONFIG_EP_CMND_CAP       0
  #define FORCE_SAVE_CONTEXT_CAP   0
  #define COMPLNC_TRANS_CAP        0
  #define LARGE_ESIT_PAYLOAD_CAP   0
  #define CONFIG_INFO_CAP          0
#endif

#define XHCI_PAGE_SIZE    1  // Page size operational register value

#define DOORBELL_OFFSET   0x800

#define RUNTIME_OFFSET    0x600

#define PORT_SET_OFFSET  (0x400 + OPS_REGS_OFFSET)

/************************************************************************************************/

#if ((VERSION_MAJOR > 1) ||                                                              \
    ((VERSION_MAJOR == 0) && ((VERSION_MINOR != 0x95) && (VERSION_MINOR != 0x96))) ||    \
    ((VERSION_MAJOR == 1) && ((VERSION_MINOR != 0x00) && (VERSION_MINOR != 0x10))))
#  error "Unknown Controller Version number specified."
#endif

#if (MAX_SCRATCH_PADS > 1023)
#  error "MAX_SCRATCH_PADS must be 0 to 1023."
#endif

#if (SCATCH_PAD_RESTORE && (MAX_SCRATCH_PADS == 0))
#  error "Must specify amount of scratch pad buffers to use."
#endif

#if ((MAX_SCRATCH_PADS > 0) && !SCATCH_PAD_RESTORE)
#  error "Must set SCATCH_PAD_RESTORE to 1 if MAX_SCRATCH_PADS > 0"
#endif

#if ((PARSE_ALL_EVENT == 0) && (VERSION_MAJOR > 0))
#  error "PARSE_ALL_EVENT must be 1 in version 1.0 and above"
#endif

#if ((SEC_DOMAIN_BAND == 0) && (VERSION_MAJOR > 0))
#  error "SEC_DOMAIN_BAND must be 1 in version 1.0 and above"
#endif

#if ((LAT_TOL_MSGING_CAP == 1) && ((VERSION_MAJOR < 1) || (VERSION_MINOR < 0)))
#  error "LAT_TOL_MSGING_CAP must be used with in version 1.1 and above"
#endif

#if ((NO_SSD_SUPPORT == 1) && ((VERSION_MAJOR < 1) || (VERSION_MINOR < 0)))
#  error "NO_SSD_SUPPORT must be used with in version 1.1 and above"
#endif

// Each controller supports its own number of ports.  We must adhere to that for now.
//  (The Extended Capabilities register set is hardcoded for this as of now.)
//  As long as BX_N_USB_XHCI_PORTS was defined as greater than what we have now, then
//  we are fine.
// Note: BX_N_USB_XHCI_PORTS should have been defined as twice the amount of ports wanted.
//  ie.: Each physical port (socket) has two defined port register sets.  One for USB3, one for USB2
// Only one port type may be used at a time.  Port0 or Port1, not both.  If Port0 is used, then
//  Port1 must be vacant.
#define BX_N_USB_XHCI_PORTS 4

// xHCI speed values
#define SPEED_FULL   1
#define SPEED_LOW    2
#define SPEED_HI     3
#define SPEED_SUPER  4

#define USB2 0
#define USB3 1

// our saved ring members
struct RING_MEMBERS {
  struct {
    Bit64u dq_pointer;
    bool   rcs;
  } command_ring;
  struct {
    bool     rcs;
    unsigned trb_count;
    unsigned count;
    Bit64u   cur_trb;
    struct {
      Bit64u addr;
      Bit32u size;
      Bit32u resv;
    } entrys[(1<<MAX_SEG_TBL_SZ_EXP)];
  } event_rings[INTERRUPTERS];
};

struct SLOT_CONTEXT {
  unsigned entries;
  bool     hub;
  bool     mtt;
  unsigned speed;
  Bit32u   route_string;
  unsigned num_ports;
  unsigned rh_port_num;
  unsigned max_exit_latency;
  unsigned int_target;
  unsigned ttt;
  unsigned tt_port_num;
  unsigned tt_hub_slot_id;
  unsigned slot_state;
  unsigned device_address;
};

struct EP_CONTEXT {
  unsigned interval;
  bool     lsa;
  unsigned max_pstreams;
  unsigned mult;
  unsigned ep_state;
  unsigned max_packet_size;
  unsigned max_burst_size;
  bool     hid;
  unsigned ep_type;
  unsigned cerr;
  Bit64u   tr_dequeue_pointer;
  bool     dcs;
  unsigned max_esit_payload;
  unsigned average_trb_len;
};

struct HC_SLOT_CONTEXT {
  bool enabled;
  bool sent_address;  // have we sent a SET_ADDRESS command yet?
  struct SLOT_CONTEXT slot_context;
  struct {
    struct EP_CONTEXT   ep_context;
    // our internal registers follow
    Bit32u  edtla;
    Bit64u  enqueue_pointer;
    bool    rcs;
    bool    retry;
    int     retry_counter;
  } ep_context[32];  // first one is ignored by controller.
};

// TRB Types
enum { NORMAL=1, SETUP_STAGE, DATA_STAGE, STATUS_STAGE, ISOCH, LINK, EVENT_DATA, NO_OP,
       ENABLE_SLOT=9, DISABLE_SLOT, ADDRESS_DEVICE, CONFIG_EP, EVALUATE_CONTEXT, RESET_EP,
       STOP_EP=15, SET_TR_DEQUEUE, RESET_DEVICE, FORCE_EVENT, DEG_BANDWIDTH, SET_LAT_TOLERANCE,
       GET_PORT_BAND=21, FORCE_HEADER, NO_OP_CMD,  // 24 - 31 = reserved
       TRANS_EVENT=32, COMMAND_COMPLETION, PORT_STATUS_CHANGE, BANDWIDTH_REQUEST, DOORBELL_EVENT,
       HOST_CONTROLLER_EVENT=37, DEVICE_NOTIFICATION, MFINDEX_WRAP, 
       // 40 - 47 = reserved
       // 48 - 63 = Vendor Defined
};

// event completion codes
enum { TRB_SUCCESS=1, DATA_BUFFER_ERROR, BABBLE_DETECTION, TRANSACTION_ERROR, TRB_ERROR, STALL_ERROR,
       RESOURCE_ERROR=7, BANDWIDTH_ERROR, NO_SLOTS_ERROR, INVALID_STREAM_TYPE, SLOT_NOT_ENABLED, EP_NOT_ENABLED,
       SHORT_PACKET=13, RING_UNDERRUN, RUNG_OVERRUN, VF_EVENT_RING_FULL, PARAMETER_ERROR, BANDWITDH_OVERRUN,
       CONTEXT_STATE_ERROR=19, NO_PING_RESPONSE, EVENT_RING_FULL, INCOMPATIBLE_DEVICE, MISSED_SERVICE,
       COMMAND_RING_STOPPED=24, COMMAND_ABORTED, STOPPED, STOPPER_LENGTH_ERROR, RESERVED, ISOCH_BUFFER_OVERRUN,
       EVERN_LOST=32, UNDEFINED, INVALID_STREAM_ID, SECONDARY_BANDWIDTH, SPLIT_TRANSACTION
       /* 37 - 191 reserved */
       /* 192 - 223 vender defined errors */
       /* 224 - 225 vendor defined info */
};

// Port Link States
enum { PLS_U0 = 0, PLS_U1, PLS_U2, PLS_U3_SUSPENDED, PLS_DISABLED, PLS_RXDETECT, PLS_INACTIVE, PLS_POLLING,
       PLS_RECOVERY = 8, PLS_HOT_RESET, PLS_COMPLIANCE, PLS_TEST_MODE,
       /* 12 - 14 reserved */
       PLS_RESUME = 15
};


// Reset type
#define HOT_RESET   0
#define WARM_RESET  1

// Direction
#define EP_DIR_OUT  0
#define EP_DIR_IN   1

// Slot State
#define SLOT_STATE_DISABLED_ENABLED  0
#define SLOT_STATE_DEFAULT           1
#define SLOT_STATE_ADDRESSED         2
#define SLOT_STATE_CONFIGURED        3

// EP State
#define EP_STATE_DISABLED 0
#define EP_STATE_RUNNING  1
#define EP_STATE_HALTED   2
#define EP_STATE_STOPPED  3
#define EP_STATE_ERROR    4

// NEC Vendor specific TRB types
#define NEC_TRB_TYPE_CMD_COMP 48
#define NEC_TRB_TYPE_GET_FW   49
#define NEC_TRB_TYPE_GET_UN   50
  #define NEC_MAGIC           0x49434878
#define NEC_FW_MAJOR(v)       (((v) & 0x0000FF00) >> 8)
#define NEC_FW_MINOR(v)       (((v) & 0x000000FF) >> 0)

#define TRB_GET_STYPE(x)     (((x) & (0x1F << 16)) >> 16)
#define TRB_SET_STYPE(x)     (((x) & 0x1F) << 16)
#define TRB_GET_TYPE(x)      (((x) & (0x3F << 10)) >> 10)
#define TRB_SET_TYPE(x)      (((x) & 0x3F) << 10)
#define TRB_GET_COMP_CODE(x) (((x) & (0xFF << 24)) >> 24)
#define TRB_SET_COMP_CODE(x) (((x) & 0xFF) << 24)
#define TRB_GET_SLOT(x)      (((x) & (0xFF << 24)) >> 24)
#define TRB_SET_SLOT(x)      (((x) & 0xFF) << 24)
#define TRB_GET_TDSIZE(x)    (((x) & (0x1F << 17)) >> 17)
#define TRB_SET_TDSIZE(x)    (((x) & 0x1F) << 17)
#define TRB_GET_EP(x)        (((x) & (0x1F << 16)) >> 16)
#define TRB_SET_EP(x)        (((x) & 0x1F) << 16)

#define TRB_GET_TARGET(x)    (((x) & (0x3FF << 22)) >> 22)
#define TRB_GET_TX_LEN(x)     ((x) & 0x1FFFF)
#define TRB_GET_TOGGLE(x)    (((x) & (1<<1)) >> 1)

#define TRB_DC(x)            (((x) & (1<<9)) >> 9)
#define TRB_IS_IMMED_DATA(x) (((x) & (1<<6)) >> 6)
#define TRB_IOC(x)           (((x) & (1<<5)) >> 5)
#define TRB_CHAIN(x)         (((x) & (1<<4)) >> 4)
#define TRB_SPD(x)           (((x) & (1<<2)) >> 2)
#define TRB_TOGGLE(x)        (((x) & (1<<1)) >> 1)
#define TRB_TX_TYPE(x)       (((x) == 2) ? USB_TOKEN_OUT : USB_TOKEN_IN)
#define TRB_GET_DIR(x)       (((x) & (1<<16)) ? USB_TOKEN_IN : USB_TOKEN_OUT)

struct TRB {
  Bit64u parameter;
  Bit32u status;
  Bit32u command;
};

typedef struct {

  struct XHCI_CAP_REGS {
    Bit32u HcCapLength;
    Bit32u HcSParams1;
    Bit32u HcSParams2;
    Bit32u HcSParams3;
    Bit32u HcCParams1;
#if ((VERSION_MAJOR == 1) && (VERSION_MINOR >= 1))
    Bit32u HcCParams2;
#endif
    Bit32u DBOFF;
    Bit32u RTSOFF;
  } cap_regs;

  struct XHCI_OP_REGS {
    struct {
      Bit32u RsvdP1;         // 18/20 bit reserved and preserved   = 0x000000       RW
#if ((VERSION_MAJOR == 1) && (VERSION_MINOR >= 0x10))
      bool   cme;            //  1 bit Max Exit Latecy to Large    = 0b             RW
      bool   spe;            //  1 bit Generate Short Packet Comp  = 0b             RW
#endif
      bool   eu3s;           //  1 bit Enable U3 MFINDEX Stop      = 0b             RW
      bool   ewe;            //  1 bit Enable Wrap Event           = 0b             RW
      bool   crs;            //  1 bit Controller Restore State    = 0b             RW
      bool   css;            //  1 bit Controller Save State       = 0b             RW
      bool   lhcrst;         //  1 bit Light HC Reset              = 0b             RW or RO (HCCPARAMS:LHRC)
      Bit8u  RsvdP0;         //  1 bit reserved and preserved      = 000b           RW
      bool   hsee;           //  1 bit Host System Error Enable    = 0b             RW
      bool   inte;           //  1 bit Interrupter Enable          = 0b             RW
      bool   hcrst;          //  1 bit HC Reset                    = 0b             RW
      bool   rs;             //  1 bit Run Stop                    = 0b             RW
    } HcCommand;             //                                    = 0x00000000
    struct {
      Bit32u RsvdZ1;         // 19 bit reserved and zero'd         = 0x000000       RW
      bool   hce;            //  1 bit Host Controller Error       = 0b             RO
      bool   cnr;            //  1 bit Controller Not Ready        = 0b             R0
      bool   sre;            //  1 bit Save/Restore Error          = 0b             RW1C
      bool   rss;            //  1 bit Restore State Status        = 0b             RO
      bool   sss;            //  1 bit Save State Status           = 0b             RO
      Bit8u  RsvdZ0;         //  3 bit reserved and zero'd         = 0x0            RW
      bool   pcd;            //  1 bit Port Change Detect          = 0b             RW1C
      bool   eint;           //  1 bit Event Interrupt             = 0b             RW1C
      bool   hse;            //  1 bit Host System Error           = 0b             RW1C
      bool   RsvdZ2;         //  1 bit reserved and zero'd         = 0b             RW
      bool   hch;            //  1 bit HCHalted                    = 1b             RO
    } HcStatus;              //                                    = 0x00000001
    struct {
      Bit16u  Rsvd;          // 16 bit reserved                    = 0x0000         RO
      Bit16u  pagesize;      // 16 bit reserved                    = 0x0001         RO
    } HcPageSize;            //                                    = 0x00000001
    struct {
      Bit16u  RsvdP;         // 16 bit reserved and presserved     = 0x0000         RW
      bool n15;              //  1 bit N15                         = 0              RW
      bool n14;              //  1 bit N14                         = 0              RW
      bool n13;              //  1 bit N13                         = 0              RW
      bool n12;              //  1 bit N12                         = 0              RW
      bool n11;              //  1 bit N11                         = 0              RW
      bool n10;              //  1 bit N10                         = 0              RW
      bool n9;               //  1 bit N9                          = 0              RW
      bool n8;               //  1 bit N8                          = 0              RW
      bool n7;               //  1 bit N7                          = 0              RW
      bool n6;               //  1 bit N6                          = 0              RW
      bool n5;               //  1 bit N5                          = 0              RW
      bool n4;               //  1 bit N4                          = 0              RW
      bool n3;               //  1 bit N3                          = 0              RW
      bool n2;               //  1 bit N2                          = 0              RW
      bool n1;               //  1 bit N1                          = 0              RW
      bool n0;               //  1 bit N0                          = 0              RW
    } HcNotification;        //                                    = 0x00000000
    struct {
      Bit64u crc;            // 64 bit hi order address            = 0x00000000     RW
      Bit8u  RsvdP;          //  2 bit reserved and preserved      = 00b            RW
      bool   crr;            //  1 bit Command Ring Running        = 0              RO
      bool   ca;             //  1 bit Command Abort               = 0              RW1S
      bool   cs;             //  1 bit Command Stop                = 0              RW1S
      bool   rcs;            //  1 bit Ring Cycle State            = 0              RW
    } HcCrcr;
    struct {
      Bit64u dcbaap;         // 64 bit hi order address            = 0x00000000     RW
      Bit8u  RsvdZ;          //  6 bit reserved and zero'd         = 000000b        RW
    } HcDCBAAP;
    struct {
      Bit32u RsvdP;          // 22/24 bit reserved and preserved   = 0x000000       RW
#if ((VERSION_MAJOR == 1) && (VERSION_MINOR >= 0x10))
      bool   u3e;            //  1 bit U3 Entry Enable             = 0              RW
      bool   cie;            //  1 bit Config Info Enable          = 0              RW
#endif
      Bit8u   MaxSlotsEn;    //  8 bit Max Device Slots Enabled    = 0x00           RW
    } HcConfig;
  } op_regs;

  struct {
    // our data
    usb_device_c *device; // device connected to this port
    bool is_usb3;         // set if usb3 port, cleared if usb2 port.
    bool has_been_reset;  // set if the port has been reset aftet powered up.

    struct {
      bool  wpr;               //  1 bit Warm Port Reset             = 0b             RW or RsvdZ
      bool  dr;                //  1 bit Device Removable            = 0b             RO
      Bit8u RsvdZ1;            //  2 bit Reserved and Zero'd         = 00b            RW
      bool  woe;               //  1 bit Wake on Over Current Enable = 0b             RW
      bool  wde;               //  1 bit Wake on Disconnect Enable   = 0b             RW
      bool  wce;               //  1 bit Wake on Connect Enable      = 0b             RW
      bool  cas;               //  1 bit Cold Attach Status          = 0b             RO
      bool  cec;               //  1 bit Port Config Error Change    = 0b             RW1C or RsvdZ
      bool  plc;               //  1 bit Port Link State Change      = 0b             RW1C
      bool  prc;               //  1 bit Port Reset Change           = 0b             RW1C
      bool  occ;               //  1 bit Over Current Change         = 0b             RW1C
      bool  wrc;               //  1 bit Warm Port Reset Change      = 0b             RW1C or RsvdZ
      bool  pec;               //  1 bit Port Enabled/Disabled Change= 0b             RW1C
      bool  csc;               //  1 bit Connect Status Change       = 0b             RW1C
      bool  lws;               //  1 bit Port Link State Write Strobe= 0b             RW
      Bit8u pic;               //  2 bit Port Indicator Control      = 00b            RW
      Bit8u speed;             //  4 bit Port Speed                  = 0000b          RO
      bool  pp;                //  1 bit Port Power                  = 0b             RW
      Bit8u pls;               //  4 bit Port Link State             = 0x00           RW
      bool  pr;                //  1 bit Port Reset                  = 0b             RW
      bool  oca;               //  1 bit Over Current Active         = 0b             RO
      bool  RsvdZ0;            //  1 bit Reserved and Zero'd         = 0b             RW
      bool  ped;               //  1 bit Port Enabled/Disabled       = 0b             RW1C
      bool  ccs;               //  1 bit Current Connect Status      = 0b             RO
    } portsc;
    union {
      // if usb3 port
      struct {
        struct {
          Bit16u  RsvdP;         // 15 bit Reserved and Preserved      = 0x0000         RW
          bool    fla;           //  1 bit Force Link PM Accept        = 0x0000         RW
          Bit8u   u2timeout;     //  8 bit U2 Timeout                  = 0x0000         RW
          Bit8u   u1timeout;     //  8 bit U1 Timeout                  = 0x0000         RW
        } portpmsc;
        struct {
          Bit16u  RsvdP;         // 16 bit Reserved and Preserved      = 0x0000         RW
          Bit16u  lec;           // 16 bit Link Error Count            = 0x0000         RO
        } portli;
      } usb3;
      // if usb2 port
      struct {
        struct {
          Bit8u   tmode;         //  4 bit Test Mode                   = 0x0            RO
          Bit16u  RsvdP;         // 11 bit reserved and preseved       = 0x000          RW
          bool    hle;           //  1 bit hardware LPM enable         = 0b             RW
          Bit8u   l1dslot;       //  8 bit L1 Device Slot              = 0x00           RW
          Bit8u   hird;          //  4 bit Host Initiated Resume Durat = 0x0            RW
          bool    rwe;           //  1 bit Remote Wakeup Enable        = 0b             RW
          Bit8u   l1s;           //  3 bit L1 Status                   = 000b           RO
        } portpmsc;
        struct {
          Bit32u  RsvdP;         // 32 bit reserved and preseved       = 0x00000000     RW
        } portli;
      } usb2;
    };
    struct {
      Bit8u   hirdm;             //  2 bit host initiated resume duration mode
      Bit8u   l1timeout;         //  8 bit L1 timeout
      Bit8u   hirdd;             //  4 bit host initiated resume duration deep
      Bit32u  RsvdP;             // 18 bit reserved and preseved       = 0x00000000     RW
    } porthlpmc;
  } usb_port[USB_XHCI_PORTS];

  // Extended Caps Registers
  Bit8u extended_caps[EXT_CAPS_SIZE];

  struct XHCI_RUNTIME_REGS {
    struct {
      Bit32u RsvdP;              // 18 bit reserved and preseved       = 0x00000        RW
      Bit16u index;              // 14 bit index                       = 0x0000         RO
    } mfindex;
    struct {
      struct {
        Bit32u  RsvdP;           // 30 bit reserved and preseved       = 0x00000000     RW
        bool    ie;              //  1 bit Interrupt Enable            = 0b             RW
        bool    ip;              //  1 bit Interrupt Pending           = 0b             RW1C
      } iman;
      struct {
        Bit16u  imodc;           // 16 bit Interrupter Mod Counter     = 0x0000         RW
        Bit16u  imodi;           // 16 bit Interrupter Mod Interval    = 0x0000         RW
      } imod;
      struct {
        Bit16u  RsvdP;           // 16 bit reserved and preseved       = 0x0000         RW
        Bit16u  erstabsize;      // 16 bit Event Ring Seg Table Size   = 0x0000         RW
      } erstsz;
      Bit32u  RsvdP;             // 32 bit reserved and preseved       = 0x00000000     RW
      struct {
        Bit64u  erstabadd;       // 64 bit Event Ring Seg Tab Addy     = 0x00000000     RW  (See #define below)
        Bit16u  RsvdP;           //  6 bit reserved and preseved       = 0x0000         RW
      } erstba;
      struct {
        Bit64u  eventadd;        // 64 bit Event Ring Addy hi          = 0x00000000     RW
        bool    ehb;             //  1 bit Event Handler Busy          = 0b             RW1C
        Bit8u   desi;            //  2 bit Dequeue ERST Seg Index      = 00b            RW
      } erdp;
    } interrupter[INTERRUPTERS];
  } runtime_regs;

  struct HC_SLOT_CONTEXT slots[MAX_SLOTS];  // first one is ignored by controller.

  struct RING_MEMBERS ring_members;
} bx_usb_xhci_t;

// Version 3.0.23.0 of the Renesas uPD720202 driver, even though the card is
//  version 1.00, the driver still uses bits 3:0 as RsvdP as with version 0.96
//  instead of bits 5:0 as RsvdP as with version 1.00+
#define RENESAS_ERSTABADD_BUG 1
#if ((VERSION_MAJOR < 1) || (RENESAS_ERSTABADD_BUG == 1))
  #define ERSTABADD_MASK   0x0F  // versions before 1.0 use 3:0 as preserved
#elif ((VERSION_MAJOR == 1) && (VERSION_MINOR >= 0x00))
  #define ERSTABADD_MASK   0x3F  // versions 1.0 and above use 5:0 as preserved
#else
  #error "ERSTABADD_MASK not defined"
#endif

class bx_usb_xhci_c : public bx_pci_device_c {
public:
  bx_usb_xhci_c();
  virtual ~bx_usb_xhci_c();
  virtual void init(void);
  virtual void reset(unsigned);
  virtual void register_state(void);
  virtual void after_restore_state(void);

  virtual void pci_write_handler(Bit8u address, Bit32u value, unsigned io_len);

  void event_handler(int event, USBPacket *packet, int port);

private:
  bx_usb_xhci_t hub;
  Bit8u         devfunc;
  Bit8u         device_change;
  int           rt_conf_id;
  int           xhci_timer_index;
  USBAsync      *packets;

  static void reset_hc();
  static void reset_port(int);
  static void reset_port_usb3(int, const int);
  static bool save_hc_state(void);
  static bool restore_hc_state(void);

  static void update_irq(unsigned interrupter);

  static void init_device(Bit8u port, bx_list_c *portconf);
  static void remove_device(Bit8u port);
  static bool usb_set_connect_status(Bit8u port, bool connected);

  static int  broadcast_packet(USBPacket *p, const int port);
  static void xhci_timer_handler(void *);
  void xhci_timer(void);

  static void process_transfer_ring(const int slot, const int ep);
  static void process_command_ring(void);
  static void write_event_TRB(const unsigned interrupter, const Bit64u parameter, const Bit32u status, 
                              const Bit32u command, const bool fire_int);
  static Bit32u NEC_verification(const Bit64u parameter);
  static void init_event_ring(const unsigned interrupter);
  static void read_TRB(bx_phy_address addr, struct TRB *trb);
  static void write_TRB(bx_phy_address addr, const Bit64u parameter, const Bit32u status, const Bit32u command);
  static void update_slot_context(const int slot);
  static void update_ep_context(const int slot, const int ep);
  static void dump_slot_context(const Bit32u *context, const int slot);
  static void dump_ep_context(const Bit32u *context, const int slot, const int ep);
  static void copy_slot_from_buffer(struct SLOT_CONTEXT *slot_context, const Bit8u *buffer);
  static void copy_ep_from_buffer(struct EP_CONTEXT *ep_context, const Bit8u *buffer);
  static void copy_slot_to_buffer(Bit32u *buffer, const int slot);
  static void copy_ep_to_buffer(Bit32u *buffer, const int slot, const int ep);
  static bool validate_slot_context(const struct SLOT_CONTEXT *slot_context);
  static bool validate_ep_context(const struct EP_CONTEXT *ep_context, int speed, int ep_num);
  static int  create_unique_address(const int slot);
  static int  send_set_address(const int addr, const int port_num);

  static void dump_xhci_core(const int slots, const int eps);

#if BX_USE_USB_XHCI_SMF
  static bool read_handler(bx_phy_address addr, unsigned len, void *data, void *param);
  static bool write_handler(bx_phy_address addr, unsigned len, void *data, void *param);
#else
  bool read_handler(bx_phy_address addr, unsigned len, void *data, void *param);
  bool write_handler(bx_phy_address addr, unsigned len, void *data, void *param);
#endif

  static void runtime_config_handler(void *);
  void runtime_config(void);

  static Bit64s usb_param_handler(bx_param_c *param, bool set, Bit64s val);
  static bool usb_param_enable_handler(bx_param_c *param, bool en);
};

#endif  // BX_IODEV_USB_XHCI_H
