//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
// 
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// 
// Module Name:  
//     CHW.hpp
// 
// Abstract: Provides interface to OHCI host controller
// 
// Notes: 
//

#ifndef __CHW_HPP__
#define __CHW_HPP__

#include <globals.hpp>
#include <hcd.hpp>
class COhcd;
class CHW;

typedef const DWORD C_DWORD;

struct HcRegisters {
    struct {
        DWORD REV:8;            // OHCI revision. Must be 0x10.
        DWORD LEGACY:1;         // Legacy Support.
        DWORD :23;
    } const HcRevision;
    struct HcControl {          // All fields default to zero at reset.
        DWORD CBSR:2;           // Control/Bulk Service Ratio (CBSR+1):1
        DWORD PLE:1;            // Periodic List Enable
        DWORD IE:1;             // Isochronous Enable
        DWORD CLE:1;            // Control List Enable
        DWORD BLE:1;            // Bulk List Enable
        DWORD HCFS:2;           // Host Controller Functional State
        enum { HCFS_RESET=0, HCFS_RESUME=1, HCFS_OPERATIONAL=2, HCFS_SUSPEND=3 };
        DWORD IR:1;             // Interrupt Routing (unused)
        DWORD RWC:1;            // Remote Wakeup Connected (unusable - see spec)
        DWORD RWE:1;            // Remote Wakeup Enable (unused)
        DWORD :21;
    } HcControl;
    struct {                    // All fields default to zero at reset.
        DWORD HCR:1;            // Host Controller Reset (auto-clear)
        DWORD CLF:1;            // Control List Filled
        DWORD BLF:1;            // Bulk List Filled
        DWORD OCR:1;            // Ownership Change Request (unused)
        DWORD :12;
        DWORD SOC:2;            // Scheduling Overrun Count
        DWORD :14;
    } HcCommandStatus;
    union HcInterruptStatus {
        struct {                // Write 1 to clear each condition bit.
            DWORD SO:1;         // Scheduling Overrun
            DWORD WDH:1;        // Writeback DoneHead
            DWORD SF:1;         // Start of Frame
            DWORD RD:1;         // Resume Detected
            DWORD UE:1;         // Unrecoverable Error
            DWORD FNO:1;        // Frame Number Overflow
            DWORD RHSC:1;       // Root Hub Status Change
            DWORD :23;
            DWORD OC:1;         // Ownership Change (unused)
            DWORD :1;
        };
        DWORD reg;              // the entire register at once
    } HcInterruptStatus;
    union OHCI_Ints {          // Write 1 to enable each interrupt.
      struct {
        DWORD SO:1;             // Scheduling Overrun
        DWORD WDH:1;            // Writeback DoneHead
        DWORD SF:1;             // Start of Frame
        DWORD RD:1;             // Resume Detected
        DWORD UE:1;             // Unrecoverable Error
        DWORD FNO:1;            // Frame Number Overflow
        DWORD RHSC:1;           // Root Hub Status Change
        DWORD :23;
        DWORD OC:1;             // Ownership Change (unused)
        DWORD MIE:1;            // Master Interrupt Enable
      };
      DWORD reg;
    } HcInterruptEnable;
    union OHCI_Ints             // Write 1 to diable each interrupt
      HcInterruptDisable;

    DWORD HcHCCA;               // PAddr of the HCCA. Must be 256-byte aligned.
    DWORD HcPeriodCurrentED;    // PAddr of current Isoch or Intr ED. 16-byte aligned.
    DWORD HcControlHeadED;      // PAddr of first Control ED. 16-byte aligned.
    DWORD HcControlCurrentED;   // PAddr of current Control ED. 16-byte aligned.
    DWORD HcBulkHeadED;         // Like Control but for Bulk.
    DWORD HcBulkCurrentED;      // Like Control but for Bulk.
    C_DWORD HcDoneHead;         // PAddr of last completed TD.
    
    struct HcFmInterval {
        DWORD FI:14;            // Frame Interval (nominally 11999 or 0x2EDF)
        DWORD :2;
        DWORD FSMPS:15;         // FS Largest Data Packet (see OHCI section 5.4)
        DWORD FIT:1;            // Frame Interval Toggle
    } HcFmInterval;
    C_DWORD HcFmRemaining;      // (unused)
    struct {
        C_DWORD FN:16;          // Frame Number (LSW)
        C_DWORD :16;
    } HcFmNumber;
    struct {
        DWORD PS:14;            // Periodic Start
        DWORD :18;
    } HcPeriodicStart;
    struct {
        C_DWORD LST:11;         // Low Speed Threshold (nominally 0x628)
        C_DWORD :21;
    } HcLSThreshold;

    struct {
        DWORD NDP:8;            // Number of Downstream Ports
        DWORD PSM:1;            // Power Switching Mode
        DWORD NPS:1;            // No Power Switching
        DWORD DT:1;             // Device Type (must be zero)
        DWORD OCPM:1;           // Over-Current Protection Mode
        DWORD NOCP:1;           // No Over-Current Protection
        DWORD :11;
        DWORD POTPGT:8;         // Power-On to Power-Good Time (2ms units)
    } HcRhDescriptorA;
    struct {
        DWORD DR:16;            // Device Removable (bitmask, b0 reserved)
        DWORD PPCM:16;          // Port Power Control Mask (ditto)
    } HcRhDescriptorB;
    union HcRhStatus { // We can not use bit field structure it is write to set or clear
        enum { LPS=0x1,OCI=0x2,DRWE=0x8000,LPSC=0x10000,OCIC=0x20000,CRWE=0x80000000};
        DWORD reg;
    } HcRhStatus;
    union HcRhPortStatus {
        // for writing, we generally set only one bit at a time.
        enum { CPE=0x00000001, SPE=0x00000002, SPS=0x00000004, CPS=0x00000008,
               SPR=0x00000010,
               SPP=0x00000100, CPP=0x00000200,
               CCSC=0x00010000, CPESC=0x00020000, CPSSC=0x00040000, COCIC=0x00080000,
               CPRSC=0x00100000
        };
        struct {
            DWORD CCS:1;        // Current Connect Status / Clear Port Enable
            DWORD PES:1;        // Port Enable Status / Set Port Enable
            DWORD PSS:1;        // Port Suspend Status / Set Port Suspend
            DWORD POCI:1;       // Port Over-Current Indicator / Clear Port Suspend
            DWORD PRS:1;        // Port Reset Status / Set Port Reset
            DWORD :3;
            DWORD PPS:1;        // Port Power Status / Set Port Power
            DWORD LSDA:1;       // Low Speed Device Attached / Clear Port Power
            DWORD :6;
            DWORD CSC:1;        // Connect Status Change / clear CSC
            DWORD PESC:1;       // Port Enable Status Change / clear PESC
            DWORD PSSC:1;       // Port Suspend Status Change / clear PSSC
            DWORD OCIC:1;       // Over-Current Indicator Change / clear OCIC
            DWORD PRSC:1;       // Port Reset Status Change / clear PRSC
            DWORD :11;
        };
        DWORD reg;
    } HcRhPortStatus[15];
};

struct HCCA {
    DWORD HccaInterruptTable[32];
    WORD  HccaFrameNumber;
    WORD  HccaPad1;             // unused
    DWORD HccaDoneHead;         // PAddr of first TD in Done Queue. 16-byte aligned. LSb special.
    // remainder is reserved so we can ignore it.
};


// this class is an encapsulation of OHCI hardware registers.
class CHW : public CHcd
{
public:
    // ****************************************************
    // public Functions
    // ****************************************************

    // 
    // Hardware Init/Deinit routines
    //
    CHW(IN const REGISTER portBase,
                              IN const DWORD dwSysIntr,
                              IN CPhysMem * const pCPhysMem,
                              IN LPVOID pvOhcdPddObject );
    ~CHW();
    virtual BOOL   Initialize(void);
    virtual void   DeInitialize( void );
    virtual void   SignalCheckForDoneTransfers ( DWORD  ) { ASSERT(FALSE); };

    void   EnterOperationalState(void);

    void   StopHostController(void);

    enum { LIST_CONTROL=1, LIST_BULK=2, LIST_INTERRUPT=4, LIST_ISOCH=8, LIST_ALL=15 };
    void   ListControl( IN const DWORD bfList,
                               IN const BOOL  bEnable,
                               IN const BOOL  bFill );

    //
    // Functions to Query frame values
    //
    BOOL GetFrameNumber( OUT LPDWORD lpdwFrameNumber );

    BOOL GetFrameLength( OUT LPUSHORT lpuFrameLength );
    
    BOOL SetFrameLength( IN HANDLE hEvent,
                         IN USHORT uFrameLength );
    
    BOOL StopAdjustingFrame( void );

    BOOL WaitOneFrame( void );

    //
    // Root Hub Queries
    //
    virtual UCHAR GetNumberOfDownStreamPort()  {
        return m_portBase->HcRhDescriptorA.NDP ;
    }
    BOOL DidPortStatusChange( IN const UCHAR port );

    BOOL GetPortStatus( IN const UCHAR port,
                               OUT USB_HUB_AND_PORT_STATUS& rStatus );

    void GetRootHubDescriptor( OUT USB_HUB_DESCRIPTOR& descriptor );

    BOOL RootHubFeature( IN const UCHAR port,
                                IN const UCHAR setOrClearFeature,
                                IN const USHORT feature );

    BOOL ResetAndEnablePort( IN const UCHAR port );

    void DisablePort( IN const UCHAR port );

    virtual BOOL WaitForPortStatusChange (HANDLE m_hHubChanged);
    // Public Interface, called by Hub object.
    BOOL    SuspendHC();


    // ****************************************************
    // public Variables
    // ****************************************************
    PDWORD m_pControlHead;
    PDWORD m_pBulkHead;
    PDWORD m_pInterruptTable;

private:
    // ****************************************************
    // private Functions
    // ****************************************************

    static DWORD CALLBACK CeResumeThreadStub( IN PVOID context );
    DWORD CeResumeThread( );
    
    static DWORD CALLBACK UsbInterruptThreadStub( IN PVOID context );
    DWORD UsbInterruptThread();

    static DWORD CALLBACK UsbAdjustFrameLengthThreadStub( IN PVOID context );
    DWORD CALLBACK UsbAdjustFrameLengthThread();

    void   UpdateFrameCounter( void );
    VOID    SuspendHostController();
    VOID    ResumeHostController();
#ifdef DEBUG
    // Query Host Controller for registers, and prints contents
    DWORD dwTickCountLastTime;
    static void DumpAllRegisters(void);
#endif
    WORD lastFn;
    // ****************************************************
    // Private Variables
    // ****************************************************

    volatile HcRegisters *m_portBase;
    volatile HCCA *m_pHCCA;

    // internal frame counter variables
    CRITICAL_SECTION m_csFrameCounter;
    WORD    m_wFrameHigh;

    // interrupt thread variables
    DWORD    m_dwSysIntr;
    HANDLE   m_hUsbInterruptEvent;
    HANDLE   m_hUsbInterruptThread;
    HANDLE   m_hUsbHubChangeEvent;
    BOOL     m_fUsbInterruptThreadClosing;

    // frame length adjustment variables
    // note - use LONG because we need to use InterlockedTestExchange
    LONG     m_fFrameLengthIsBeingAdjusted;
    LONG     m_fStopAdjustingFrameLength;
    HANDLE   m_hAdjustDoneCallbackEvent;
    USHORT   m_uNewFrameLength;

    // initialization parameters for the IST to support CE resume
    // (resume from fully unpowered controller).
    CPhysMem *m_pMem;
    LPVOID    m_pPddContext;
    DWORD   m_dwCapability;
    BOOL    m_bDoResume;
public:
    DWORD   SetCapability(DWORD dwCap); 
    DWORD   GetCapability() { return m_dwCapability; };
private:
    
    BOOL g_fPowerUpFlag;
    BOOL g_fPowerResuming;

public:
    VOID PowerMgmtCallback( IN BOOL fOff );
    BOOL GetPowerUpFlag() { return g_fPowerUpFlag; };
    BOOL SetPowerUpFlag(BOOL bFlag) { return (g_fPowerUpFlag=bFlag); };
    BOOL GetPowerResumingFlag() { return g_fPowerResuming ; };
    BOOL SetPowerResumingFlag(BOOL bFlag) { return (g_fPowerResuming=bFlag) ; };
    
};
#endif // __CHW_HPP__

