///////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   PciConfig.cs
//
//  Note:
//

#define DEBUG_PCI

using System;
using StringBuilder = System.Text.StringBuilder;
using System.Collections;
using Microsoft.Singularity;

namespace Microsoft.Singularity.Io
{
    [CLSCompliant(false)]
    public class PciPort
    {
        private readonly IoPort addressPort;
        private readonly IoPort dataPort;
        private readonly ushort identifier;

        public PciPort(IoPort addressPort, IoPort dataPort, ushort identifier)
        {
            this.addressPort = addressPort;
            this.dataPort = dataPort;
            this.identifier = identifier;
        }

        public ushort Identifier { get { return this.identifier; } }

        public ushort Function
        {
            get { return (ushort)(Identifier & ((1 << 3) - 1)); }
        }

        public ushort Device
        {
            get { return (ushort)((Identifier >> 3) & ((1 << 5) - 1)); }
        }

        public ushort Bus
        {
            get { return (ushort)(Identifier >> 8); }
        }

        public virtual uint Read32(int offset)
        {
            if ((offset & 0x3) != 0) {
                throw new Exception("BAD_OFFSET");
            }

            uint config = (((uint)offset & 0xfc) |
                           ((uint)identifier << 8) |
                           ((uint)1 << 31));

            addressPort.Write32(config);
            return dataPort.Read32();
        }

        public virtual ushort Read16(int offset)
        {
            if ((offset & 0x1) != 0) {
                throw new Exception("BAD_OFFSET");
            }

            uint config = (((uint)offset & 0xfc) |
                           ((uint)identifier << 8) |
                           ((uint)1 << 31));

            addressPort.Write32(config);
            return dataPort.Read16((uint)(offset & 0x2));
        }

        public virtual byte Read8(int offset)
        {
            uint config = (((uint)offset & 0xfc) |
                           ((uint)identifier << 8) |
                           ((uint)1 << 31));

            addressPort.Write32(config);
            return dataPort.Read8((uint)(offset & 0x3));
        }

        public virtual void Write32(int offset, uint value)
        {
            if ((offset & 0x3) != 0) {
                throw new Exception("BAD_OFFSET");
            }

            uint config = (((uint)offset & 0xfc) |
                           ((uint)identifier << 8) |
                           ((uint)1 << 31));

            addressPort.Write32(config);
            dataPort.Write32(value);
        }

        public virtual void Write16(int offset, ushort value)
        {
            if ((offset & 0x1) != 0) {
                throw new Exception("BAD_OFFSET");
            }

            uint config = (((uint)offset & 0xfc) |
                           ((uint)identifier << 8) |
                           ((uint)1 << 31));

            addressPort.Write32(config);
            dataPort.Write16((uint)(offset & 0x2), value);
        }

        public virtual void Write8(int offset, byte value)
        {
            uint config = (((uint)offset & 0xff) |
                           ((uint)identifier << 8) |
                           ((uint)1 << 31));

            addressPort.Write32(config);
            dataPort.Write8((uint)(offset & 0x3), value);
        }
    }
} // end namespace Microsoft.Singularity.Io
