/*
 * domain_addr.h: helper APIs for managing domain device addresses
 *
 * Copyright (C) 2006-2016 Red Hat, Inc.
 * Copyright (C) 2006 Daniel P. Berrange
 *
 * 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.1 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, see
 * <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "domain_conf.h"

#define VIR_PCI_ADDRESS_SLOT_LAST 31
#define VIR_PCI_ADDRESS_FUNCTION_LAST 7

typedef enum {
    VIR_PCI_ADDRESS_EXTENSION_NONE = 0, /* no extension */
    VIR_PCI_ADDRESS_EXTENSION_ZPCI = 1 << 0, /* zPCI support */
} virPCIDeviceAddressExtensionFlags;

typedef enum {
    VIR_PCI_CONNECT_AUTOASSIGN = 1 << 0, /* okay to autoassign a device to this controller */
    VIR_PCI_CONNECT_HOTPLUGGABLE = 1 << 1, /* is hotplug needed/supported */

    /* set for devices that can share a single slot in auto-assignment
     * (by assigning one device to each of the 8 functions on the slot)
     */
    VIR_PCI_CONNECT_AGGREGATE_SLOT = 1 << 2,

    /* kinds of devices as a bitmap so they can be combined (some PCI
     * controllers permit connecting multiple types of devices)
     */
    VIR_PCI_CONNECT_TYPE_PCI_DEVICE = 1 << 3,
    VIR_PCI_CONNECT_TYPE_PCIE_DEVICE = 1 << 4,
    VIR_PCI_CONNECT_TYPE_PCIE_ROOT_PORT = 1 << 5,
    VIR_PCI_CONNECT_TYPE_PCIE_SWITCH_UPSTREAM_PORT = 1 << 6,
    VIR_PCI_CONNECT_TYPE_PCIE_SWITCH_DOWNSTREAM_PORT = 1 << 7,
    VIR_PCI_CONNECT_TYPE_DMI_TO_PCI_BRIDGE = 1 << 8,
    VIR_PCI_CONNECT_TYPE_PCI_EXPANDER_BUS = 1 << 9,
    VIR_PCI_CONNECT_TYPE_PCIE_EXPANDER_BUS = 1 << 10,
    VIR_PCI_CONNECT_TYPE_PCI_BRIDGE = 1 << 11,
    VIR_PCI_CONNECT_TYPE_PCIE_TO_PCI_BRIDGE = 1 << 12,
} virDomainPCIConnectFlags;

/* a combination of all bits that describe the type of connections
 * allowed, e.g. PCI, PCIe, switch
 */
#define VIR_PCI_CONNECT_TYPES_MASK \
   (VIR_PCI_CONNECT_TYPE_PCI_DEVICE | VIR_PCI_CONNECT_TYPE_PCIE_DEVICE | \
    VIR_PCI_CONNECT_TYPE_PCIE_SWITCH_UPSTREAM_PORT | \
    VIR_PCI_CONNECT_TYPE_PCIE_SWITCH_DOWNSTREAM_PORT | \
    VIR_PCI_CONNECT_TYPE_PCIE_ROOT_PORT | \
    VIR_PCI_CONNECT_TYPE_DMI_TO_PCI_BRIDGE | \
    VIR_PCI_CONNECT_TYPE_PCI_EXPANDER_BUS | \
    VIR_PCI_CONNECT_TYPE_PCIE_EXPANDER_BUS | \
    VIR_PCI_CONNECT_TYPE_PCI_BRIDGE | \
    VIR_PCI_CONNECT_TYPE_PCIE_TO_PCI_BRIDGE)

/* combination of all bits that could be used to connect a normal
 * endpoint device (i.e. excluding the connection possible between an
 * upstream and downstream switch port, or a PCIe root port and a PCIe
 * port)
 */
#define VIR_PCI_CONNECT_TYPES_ENDPOINT \
   (VIR_PCI_CONNECT_TYPE_PCI_DEVICE | VIR_PCI_CONNECT_TYPE_PCIE_DEVICE)

virDomainPCIConnectFlags
virDomainPCIControllerModelToConnectType(virDomainControllerModelPCI model);

typedef struct {
    /* each function is represented by one bit, set if that function is
     * in use by a device, or clear if it isn't.
     */
    uint8_t functions;

    /* aggregate is true if this slot has only devices with
     * VIR_PCI_CONNECT_AGGREGATE assigned to its functions (meaning
     * that other devices with the same flags could also be
     * auto-assigned to the other functions)
     */
    bool aggregate;
} virDomainPCIAddressSlot;

typedef struct {
    virDomainControllerModelPCI model;
    /* flags and min/max can be computed from model, but
     * having them ready makes life easier.
     */
    virDomainPCIConnectFlags flags;
    size_t minSlot, maxSlot; /* usually 0,0 or 0,31, or 1,31 */
    /* Each bit in a slot represents one function on that slot. If the
     * bit is set, that function is in use by a device.
     */
    virDomainPCIAddressSlot slot[VIR_PCI_ADDRESS_SLOT_LAST + 1];

    /* See virDomainDeviceInfo::isolationGroup */
    unsigned int isolationGroup;

    /* See virDomainDeviceInfo::isolationGroupLocked */
    bool isolationGroupLocked;
} virDomainPCIAddressBus;
typedef virDomainPCIAddressBus *virDomainPCIAddressBusPtr;

typedef struct {
    GHashTable *uids;
    GHashTable *fids;
} virDomainZPCIAddressIds;
typedef virDomainZPCIAddressIds *virDomainZPCIAddressIdsPtr;

struct _virDomainPCIAddressSet {
    virDomainPCIAddressBus *buses;
    size_t nbuses;
    bool dryRun;          /* on a dry run, new buses are auto-added
                             and addresses aren't saved in device infos */
    /* If true, the guest can have multiple pci-root controllers */
    bool areMultipleRootsSupported;
    /* If true, the guest can use the pcie-to-pci-bridge controller */
    bool isPCIeToPCIBridgeSupported;
    virDomainZPCIAddressIdsPtr zpciIds;
};
typedef struct _virDomainPCIAddressSet virDomainPCIAddressSet;
typedef virDomainPCIAddressSet *virDomainPCIAddressSetPtr;

virDomainPCIAddressSetPtr virDomainPCIAddressSetAlloc(unsigned int nbuses,
                                                      virPCIDeviceAddressExtensionFlags extFlags);

void virDomainPCIAddressSetFree(virDomainPCIAddressSetPtr addrs);

bool virDomainPCIAddressValidate(virDomainPCIAddressSetPtr addrs,
                                 virPCIDeviceAddressPtr addr,
                                 const char *addrStr,
                                 virDomainPCIConnectFlags flags,
                                 bool fromConfig)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);


int virDomainPCIAddressBusSetModel(virDomainPCIAddressBusPtr bus,
                                   virDomainControllerModelPCI model,
                                   bool allowHotplug)
    ATTRIBUTE_NONNULL(1);

bool virDomainPCIAddressBusIsFullyReserved(virDomainPCIAddressBusPtr bus)
    ATTRIBUTE_NONNULL(1);

bool virDomainPCIAddressSlotInUse(virDomainPCIAddressSetPtr addrs,
                                  virPCIDeviceAddressPtr addr)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);

int virDomainPCIAddressExtensionReserveAddr(virDomainPCIAddressSetPtr addrs,
                                            virPCIDeviceAddressPtr addr)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);

int virDomainPCIAddressExtensionReserveNextAddr(virDomainPCIAddressSetPtr addrs,
                                                virPCIDeviceAddressPtr addr)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);

int virDomainPCIAddressReserveAddr(virDomainPCIAddressSetPtr addrs,
                                   virPCIDeviceAddressPtr addr,
                                   virDomainPCIConnectFlags flags,
                                   unsigned int isolationGroup)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);

int virDomainPCIAddressReserveNextAddr(virDomainPCIAddressSetPtr addrs,
                                       virDomainDeviceInfoPtr dev,
                                       virDomainPCIConnectFlags flags,
                                       int function)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);

int virDomainPCIAddressEnsureAddr(virDomainPCIAddressSetPtr addrs,
                                  virDomainDeviceInfoPtr dev,
                                  virDomainPCIConnectFlags flags)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);

void virDomainPCIAddressReleaseAddr(virDomainPCIAddressSetPtr addrs,
                                    virPCIDeviceAddressPtr addr)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);

void virDomainPCIAddressExtensionReleaseAddr(virDomainPCIAddressSetPtr addrs,
                                             virPCIDeviceAddressPtr addr)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);

void virDomainPCIAddressSetAllMulti(virDomainDefPtr def)
    ATTRIBUTE_NONNULL(1);

struct _virDomainCCWAddressSet {
    GHashTable *defined;
    virDomainDeviceCCWAddress next;
};
typedef struct _virDomainCCWAddressSet virDomainCCWAddressSet;
typedef virDomainCCWAddressSet *virDomainCCWAddressSetPtr;

int virDomainCCWAddressAssign(virDomainDeviceInfoPtr dev,
                              virDomainCCWAddressSetPtr addrs,
                              bool autoassign)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
void virDomainCCWAddressSetFree(virDomainCCWAddressSetPtr addrs);

char* virDomainCCWAddressAsString(virDomainDeviceCCWAddressPtr addr)
    ATTRIBUTE_NONNULL(1);

virDomainCCWAddressSetPtr
virDomainCCWAddressSetCreateFromDomain(virDomainDefPtr def)
    ATTRIBUTE_NONNULL(1);

struct _virDomainVirtioSerialController {
    unsigned int idx;
    virBitmapPtr ports;
};

typedef struct _virDomainVirtioSerialController virDomainVirtioSerialController;
typedef virDomainVirtioSerialController *virDomainVirtioSerialControllerPtr;

struct _virDomainVirtioSerialAddrSet {
    virDomainVirtioSerialControllerPtr *controllers;
    size_t ncontrollers;
};
typedef struct _virDomainVirtioSerialAddrSet virDomainVirtioSerialAddrSet;
typedef virDomainVirtioSerialAddrSet *virDomainVirtioSerialAddrSetPtr;

void
virDomainVirtioSerialAddrSetFree(virDomainVirtioSerialAddrSetPtr addrs);
virDomainVirtioSerialAddrSetPtr
virDomainVirtioSerialAddrSetCreateFromDomain(virDomainDefPtr def)
    ATTRIBUTE_NONNULL(1);
bool
virDomainVirtioSerialAddrIsComplete(virDomainDeviceInfoPtr info);
int
virDomainVirtioSerialAddrAutoAssignFromCache(virDomainDefPtr def,
                                             virDomainVirtioSerialAddrSetPtr addrs,
                                             virDomainDeviceInfoPtr info,
                                             bool allowZero)
    ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);

int
virDomainVirtioSerialAddrAutoAssign(virDomainDefPtr def,
                                    virDomainDeviceInfoPtr info,
                                    bool allowZero)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);

bool
virDomainUSBAddressPortIsValid(unsigned int *port)
    ATTRIBUTE_NONNULL(1);

void
virDomainUSBAddressPortFormatBuf(virBufferPtr buf,
                                 unsigned int *port)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);

#define VIR_DOMAIN_USB_HUB_PORTS 8

typedef struct _virDomainUSBAddressHub virDomainUSBAddressHub;
typedef virDomainUSBAddressHub *virDomainUSBAddressHubPtr;
struct _virDomainUSBAddressHub {
    /* indexes are shifted by one:
     * ports[0] represents port 1, because ports are numbered from 1 */
    virBitmapPtr portmap;
    size_t nports;
    virDomainUSBAddressHubPtr *ports;
};

struct _virDomainUSBAddressSet {
    /* every <controller type='usb' index='i'> is represented
     * as a hub at buses[i] */
    virDomainUSBAddressHubPtr *buses;
    size_t nbuses;
};
typedef struct _virDomainUSBAddressSet virDomainUSBAddressSet;
typedef virDomainUSBAddressSet *virDomainUSBAddressSetPtr;

virDomainUSBAddressSetPtr virDomainUSBAddressSetCreate(void);

int virDomainUSBAddressSetAddControllers(virDomainUSBAddressSetPtr addrs,
                                         virDomainDefPtr def)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
int
virDomainUSBAddressSetAddHub(virDomainUSBAddressSetPtr addrs,
                             virDomainHubDefPtr hub)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
size_t
virDomainUSBAddressCountAllPorts(virDomainDefPtr def);
void virDomainUSBAddressSetFree(virDomainUSBAddressSetPtr addrs);

int
virDomainUSBAddressPresent(virDomainDeviceInfoPtr info,
                           void *data)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
int
virDomainUSBAddressReserve(virDomainDeviceInfoPtr info,
                           void *data)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);

int
virDomainUSBAddressAssign(virDomainUSBAddressSetPtr addrs,
                          virDomainDeviceInfoPtr info)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);

int
virDomainUSBAddressEnsure(virDomainUSBAddressSetPtr addrs,
                          virDomainDeviceInfoPtr info)
    ATTRIBUTE_NONNULL(2);

int
virDomainUSBAddressRelease(virDomainUSBAddressSetPtr addrs,
                           virDomainDeviceInfoPtr info)
    ATTRIBUTE_NONNULL(2);
