/*
 * Copyright (c) 1997-2003 by The XFree86 Project, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of the copyright holder(s)
 * and author(s) shall not be used in advertising or otherwise to promote
 * the sale, use or other dealings in this Software without prior written
 * authorization from the copyright holder(s) and author(s).
 */

/*
 * This file contains the interfaces to the bus-specific code
 */
#ifdef HAVE_XORG_CONFIG_H
#include <xorg-config.h>
#endif

#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <X11/X.h>
#include <pciaccess.h>
#include "os.h"
#include "Pci.h"
#include "xf86.h"
#include "xf86Priv.h"
#include "dirent.h"             /* DIR, FILE type definitions */

/* Bus-specific headers */
#include "xf86Bus.h"

#define XF86_OS_PRIVS
#include "xf86_OSproc.h"

#define PCI_VENDOR_GENERIC		0x00FF

/* Bus-specific globals */
int pciSlotClaimed = 0;

#define PCIINFOCLASSES(c) \
    ( (((c) & 0x00ff0000) == (PCI_CLASS_PREHISTORIC << 16)) \
      || (((c) & 0x00ff0000) == (PCI_CLASS_DISPLAY << 16)) \
      || ((((c) & 0x00ffff00) \
	   == ((PCI_CLASS_MULTIMEDIA << 16) | (PCI_SUBCLASS_MULTIMEDIA_VIDEO << 8)))) \
      || ((((c) & 0x00ffff00) \
	   == ((PCI_CLASS_PROCESSOR << 16) | (PCI_SUBCLASS_PROCESSOR_COPROC << 8)))) )

/*
 * PCI classes that have messages printed always.  The others are only
 * have a message printed when the vendor/dev IDs are recognised.
 */
#define PCIALWAYSPRINTCLASSES(c) \
    ( (((c) & 0x00ffff00) \
       == ((PCI_CLASS_PREHISTORIC << 16) | (PCI_SUBCLASS_PREHISTORIC_VGA << 8))) \
      || (((c) & 0x00ff0000) == (PCI_CLASS_DISPLAY << 16)) \
      || ((((c) & 0x00ffff00) \
	   == ((PCI_CLASS_MULTIMEDIA << 16) | (PCI_SUBCLASS_MULTIMEDIA_VIDEO << 8)))) )

#define IS_VGA(c) \
    (((c) & 0x00ffff00) \
	 == ((PCI_CLASS_DISPLAY << 16) | (PCI_SUBCLASS_DISPLAY_VGA << 8)))

static struct pci_slot_match xf86IsolateDevice = {
    PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, 0
};

/*
 * xf86Bus.c interface
 */

void
xf86PciProbe(void)
{
    int i = 0, k;
    int num = 0;
    struct pci_device *info;
    struct pci_device_iterator *iter;
    struct pci_device **xf86PciVideoInfo = NULL;

    if (!xf86scanpci()) {
        xf86PciVideoInfo = NULL;
        return;
    }

    iter = pci_slot_match_iterator_create(&xf86IsolateDevice);
    while ((info = pci_device_next(iter)) != NULL) {
        if (PCIINFOCLASSES(info->device_class)) {
            num++;
            xf86PciVideoInfo = xnfreallocarray(xf86PciVideoInfo,
                                               num + 1,
                                               sizeof(struct pci_device *));
            xf86PciVideoInfo[num] = NULL;
            xf86PciVideoInfo[num - 1] = info;

            pci_device_probe(info);
            if (primaryBus.type == BUS_NONE && pci_device_is_boot_vga(info)) {
                primaryBus.type = BUS_PCI;
                primaryBus.id.pci = info;
            }
            info->user_data = 0;
        }
    }
    free(iter);

    /* If we haven't found a primary device try a different heuristic */
    if (primaryBus.type == BUS_NONE && num) {
        for (i = 0; i < num; i++) {
            uint16_t command;

            info = xf86PciVideoInfo[i];
            pci_device_cfg_read_u16(info, &command, 4);

            if ((command & PCI_CMD_MEM_ENABLE)
                && ((num == 1) || IS_VGA(info->device_class))) {
                if (primaryBus.type == BUS_NONE) {
                    primaryBus.type = BUS_PCI;
                    primaryBus.id.pci = info;
                }
                else {
                    xf86Msg(X_NOTICE,
                            "More than one possible primary device found\n");
                    primaryBus.type ^= (BusType) (-1);
                }
            }
        }
    }

    /* Print a summary of the video devices found */
    for (k = 0; k < num; k++) {
        const char *prim = " ";
        Bool memdone = FALSE, iodone = FALSE;

        info = xf86PciVideoInfo[k];

        if (!PCIALWAYSPRINTCLASSES(info->device_class))
            continue;

        if (xf86IsPrimaryPci(info))
            prim = "*";

        xf86Msg(X_PROBED, "PCI:%s(%u@%u:%u:%u) %04x:%04x:%04x:%04x ", prim,
                info->bus, info->domain, info->dev, info->func,
                info->vendor_id, info->device_id,
                info->subvendor_id, info->subdevice_id);

        xf86ErrorF("rev %d", info->revision);

        for (i = 0; i < 6; i++) {
            struct pci_mem_region *r = &info->regions[i];

            if (r->size && !r->is_IO) {
                if (!memdone) {
                    xf86ErrorF(", Mem @ ");
                    memdone = TRUE;
                }
                else
                    xf86ErrorF(", ");
                xf86ErrorF("0x%08lx/%ld", (long) r->base_addr, (long) r->size);
            }
        }

        for (i = 0; i < 6; i++) {
            struct pci_mem_region *r = &info->regions[i];

            if (r->size && r->is_IO) {
                if (!iodone) {
                    xf86ErrorF(", I/O @ ");
                    iodone = TRUE;
                }
                else
                    xf86ErrorF(", ");
                xf86ErrorF("0x%08lx/%ld", (long) r->base_addr, (long) r->size);
            }
        }

        if (info->rom_size) {
            xf86ErrorF(", BIOS @ 0x\?\?\?\?\?\?\?\?/%ld",
                       (long) info->rom_size);
        }

        xf86ErrorF("\n");
    }
    free(xf86PciVideoInfo);
}

/*
 * If the slot requested is already in use, return -1.
 * Otherwise, claim the slot for the screen requesting it.
 */

int
xf86ClaimPciSlot(struct pci_device *d, DriverPtr drvp,
                 int chipset, GDevPtr dev, Bool active)
{
    EntityPtr p = NULL;
    int num;

    if (xf86CheckPciSlot(d)) {
        num = xf86AllocateEntity();
        p = xf86Entities[num];
        p->driver = drvp;
        p->chipset = chipset;
        p->bus.type = BUS_PCI;
        p->bus.id.pci = d;
        p->active = active;
        p->inUse = FALSE;
        if (dev)
            xf86AddDevToEntity(num, dev);
        pciSlotClaimed++;

        return num;
    }
    else
        return -1;
}

/*
 * Unclaim PCI slot, e.g. if probing failed, so that a different driver can claim.
 */
void
xf86UnclaimPciSlot(struct pci_device *d, GDevPtr dev)
{
    int i;

    for (i = 0; i < xf86NumEntities; i++) {
        const EntityPtr p = xf86Entities[i];

        if ((p->bus.type == BUS_PCI) && (p->bus.id.pci == d)) {
            /* Probably the slot should be deallocated? */
            xf86RemoveDevFromEntity(i, dev);
            pciSlotClaimed--;
            p->bus.type = BUS_NONE;
            return;
        }
    }
}

/*
 * Parse a BUS ID string, and return the PCI bus parameters if it was
 * in the correct format for a PCI bus id.
 */

Bool
xf86ParsePciBusString(const char *busID, int *bus, int *device, int *func)
{
    /*
     * The format is assumed to be "bus[@domain]:device[:func]", where domain,
     * bus, device and func are decimal integers.  domain and func may be
     * omitted and assumed to be zero, although doing this isn't encouraged.
     */

    char *p, *s, *d;
    const char *id;
    int i;

    if (StringToBusType(busID, &id) != BUS_PCI)
        return FALSE;

    s = xstrdup(id);
    p = strtok(s, ":");
    if (p == NULL || *p == 0) {
        free(s);
        return FALSE;
    }
    d = strpbrk(p, "@");
    if (d != NULL) {
        *(d++) = 0;
        for (i = 0; d[i] != 0; i++) {
            if (!isdigit(d[i])) {
                free(s);
                return FALSE;
            }
        }
    }
    for (i = 0; p[i] != 0; i++) {
        if (!isdigit(p[i])) {
            free(s);
            return FALSE;
        }
    }
    *bus = atoi(p);
    if (d != NULL && *d != 0)
        *bus += atoi(d) << 8;
    p = strtok(NULL, ":");
    if (p == NULL || *p == 0) {
        free(s);
        return FALSE;
    }
    for (i = 0; p[i] != 0; i++) {
        if (!isdigit(p[i])) {
            free(s);
            return FALSE;
        }
    }
    *device = atoi(p);
    *func = 0;
    p = strtok(NULL, ":");
    if (p == NULL || *p == 0) {
        free(s);
        return TRUE;
    }
    for (i = 0; p[i] != 0; i++) {
        if (!isdigit(p[i])) {
            free(s);
            return FALSE;
        }
    }
    *func = atoi(p);
    free(s);
    return TRUE;
}

/*
 * Compare a BUS ID string with a PCI bus id.  Return TRUE if they match.
 */

Bool
xf86ComparePciBusString(const char *busID, int bus, int device, int func)
{
    int ibus, idevice, ifunc;

    if (xf86ParsePciBusString(busID, &ibus, &idevice, &ifunc)) {
        return bus == ibus && device == idevice && func == ifunc;
    }
    else {
        return FALSE;
    }
}

/*
 * xf86IsPrimaryPci() -- return TRUE if primary device
 * is PCI and bus, dev and func numbers match.
 */

Bool
xf86IsPrimaryPci(struct pci_device *pPci)
{
    /* Add max. 1 screen for the IgnorePrimary fallback path */
    if (xf86ProbeIgnorePrimary && xf86NumScreens == 0)
        return TRUE;

    if (primaryBus.type == BUS_PCI)
        return pPci == primaryBus.id.pci;
#ifdef XSERVER_PLATFORM_BUS
    if (primaryBus.type == BUS_PLATFORM)
        if (primaryBus.id.plat->pdev)
            if (MATCH_PCI_DEVICES(primaryBus.id.plat->pdev, pPci))
                return TRUE;
#endif
    return FALSE;
}

/*
 * xf86GetPciInfoForEntity() -- Get the pciVideoRec of entity.
 */
struct pci_device *
xf86GetPciInfoForEntity(int entityIndex)
{
    EntityPtr p;

    if (entityIndex >= xf86NumEntities)
        return NULL;

    p = xf86Entities[entityIndex];
    switch (p->bus.type) {
    case BUS_PCI:
        return p->bus.id.pci;
    case BUS_PLATFORM:
        return p->bus.id.plat->pdev;
    default:
        break;
    }
    return NULL;
}

/*
 * xf86CheckPciMemBase() checks that the memory base value matches one of the
 * PCI base address register values for the given PCI device.
 */
Bool
xf86CheckPciMemBase(struct pci_device *pPci, memType base)
{
    int i;

    for (i = 0; i < 6; i++)
        if (base == pPci->regions[i].base_addr)
            return TRUE;
    return FALSE;
}

/*
 * Check if the slot requested is free.  If it is already in use, return FALSE.
 */

Bool
xf86CheckPciSlot(const struct pci_device *d)
{
    int i;

    for (i = 0; i < xf86NumEntities; i++) {
        const EntityPtr p = xf86Entities[i];

        if ((p->bus.type == BUS_PCI) && (p->bus.id.pci == d)) {
            return FALSE;
        }
#ifdef XSERVER_PLATFORM_BUS
        if ((p->bus.type == BUS_PLATFORM) && (p->bus.id.plat->pdev)) {
            struct pci_device *ud = p->bus.id.plat->pdev;
            if (MATCH_PCI_DEVICES(ud, d))
                return FALSE;
        }
#endif
    }
    return TRUE;
}

#define END_OF_MATCHES(m) \
    (((m).vendor_id == 0) && ((m).device_id == 0) && ((m).subvendor_id == 0))

Bool
xf86PciAddMatchingDev(DriverPtr drvp)
{
    const struct pci_id_match *const devices = drvp->supported_devices;
    int j;
    struct pci_device *pPci;
    struct pci_device_iterator *iter;
    int numFound = 0;

    iter = pci_id_match_iterator_create(NULL);
    while ((pPci = pci_device_next(iter)) != NULL) {
        /* Determine if this device is supported by the driver.  If it is,
         * add it to the list of devices to configure.
         */
        for (j = 0; !END_OF_MATCHES(devices[j]); j++) {
            if (PCI_ID_COMPARE(devices[j].vendor_id, pPci->vendor_id)
                && PCI_ID_COMPARE(devices[j].device_id, pPci->device_id)
                && ((devices[j].device_class_mask & pPci->device_class)
                    == devices[j].device_class)) {
                if (xf86CheckPciSlot(pPci)) {
                    GDevPtr pGDev =
                        xf86AddBusDeviceToConfigure(drvp->driverName, BUS_PCI,
                                                    pPci, -1);
                    if (pGDev != NULL) {
                        /* After configure pass 1, chipID and chipRev are
                         * treated as over-rides, so clobber them here.
                         */
                        pGDev->chipID = -1;
                        pGDev->chipRev = -1;
                    }

                    numFound++;
                }

                break;
            }
        }
    }

    pci_iterator_destroy(iter);

    return numFound != 0;
}

Bool
xf86PciProbeDev(DriverPtr drvp)
{
    int i, j;
    struct pci_device *pPci;
    Bool foundScreen = FALSE;
    const struct pci_id_match *const devices = drvp->supported_devices;
    GDevPtr *devList;
    const unsigned numDevs = xf86MatchDevice(drvp->driverName, &devList);

    for (i = 0; i < numDevs; i++) {
        struct pci_device_iterator *iter;
        unsigned device_id;

        /* Find the pciVideoRec associated with this device section.
         */
        iter = pci_id_match_iterator_create(NULL);
        while ((pPci = pci_device_next(iter)) != NULL) {
            if (devList[i]->busID && *devList[i]->busID) {
                if (xf86ComparePciBusString(devList[i]->busID,
                                            ((pPci->domain << 8)
                                             | pPci->bus),
                                            pPci->dev, pPci->func)) {
                    break;
                }
            }
            else if (xf86IsPrimaryPci(pPci)) {
                break;
            }
        }

        pci_iterator_destroy(iter);

        if (pPci == NULL) {
            continue;
        }
        device_id = (devList[i]->chipID > 0)
            ? devList[i]->chipID : pPci->device_id;

        /* Once the pciVideoRec is found, determine if the device is supported
         * by the driver.  If it is, probe it!
         */
        for (j = 0; !END_OF_MATCHES(devices[j]); j++) {
            if (PCI_ID_COMPARE(devices[j].vendor_id, pPci->vendor_id)
                && PCI_ID_COMPARE(devices[j].device_id, device_id)
                && ((devices[j].device_class_mask & pPci->device_class)
                    == devices[j].device_class)) {
                int entry;

                /* Allow the same entity to be used more than once for
                 * devices with multiple screens per entity.  This assumes
                 * implicitly that there will be a screen == 0 instance.
                 *
                 * FIXME Need to make sure that two different drivers don't
                 * FIXME claim the same screen > 0 instance.
                 */
                if ((devList[i]->screen == 0) && !xf86CheckPciSlot(pPci))
                    continue;

                DebugF("%s: card at %d:%d:%d is claimed by a Device section\n",
                       drvp->driverName, pPci->bus, pPci->dev, pPci->func);

                /* Allocate an entry in the lists to be returned */
                entry = xf86ClaimPciSlot(pPci, drvp, device_id,
                                         devList[i], devList[i]->active);

                if ((entry == -1) && (devList[i]->screen > 0)) {
                    unsigned k;

                    for (k = 0; k < xf86NumEntities; k++) {
                        EntityPtr pEnt = xf86Entities[k];

                        if (pEnt->bus.type != BUS_PCI)
                            continue;
                        if (pEnt->bus.id.pci == pPci) {
                            entry = k;
                            xf86AddDevToEntity(k, devList[i]);
                            break;
                        }
                    }
                }

                if (entry != -1) {
                    if ((*drvp->PciProbe) (drvp, entry, pPci,
                                           devices[j].match_data)) {
                        foundScreen = TRUE;
                    }
                    else
                        xf86UnclaimPciSlot(pPci, devList[i]);
                }

                break;
            }
        }
    }
    free(devList);

    return foundScreen;
}

void
xf86PciIsolateDevice(const char *argument)
{
    int bus, device, func;

    if (sscanf(argument, "PCI:%d:%d:%d", &bus, &device, &func) == 3) {
        xf86IsolateDevice.domain = PCI_DOM_FROM_BUS(bus);
        xf86IsolateDevice.bus = PCI_BUS_NO_DOMAIN(bus);
        xf86IsolateDevice.dev = device;
        xf86IsolateDevice.func = func;
    }
    else
        FatalError("Invalid isolated device specification\n");
}

static Bool
pciDeviceHasBars(struct pci_device *pci)
{
    int i;

    for (i = 0; i < 6; i++)
        if (pci->regions[i].size)
            return TRUE;

    if (pci->rom_size)
        return TRUE;

    return FALSE;
}

struct Inst {
    struct pci_device *pci;
    GDevPtr dev;
    Bool foundHW;               /* PCIid in list of supported chipsets */
    Bool claimed;               /* BusID matches with a device section */
    int chip;
    int screen;
};

/**
 * Find set of unclaimed devices matching a given vendor ID.
 *
 * Used by drivers to find as yet unclaimed devices matching the specified
 * vendor ID.
 *
 * \param driverName     Name of the driver.  This is used to find Device
 *                       sections in the config file.
 * \param vendorID       PCI vendor ID of associated devices.  If zero, then
 *                       the true vendor ID must be encoded in the \c PCIid
 *                       fields of the \c PCIchipsets entries.
 * \param chipsets       Symbol table used to associate chipset names with
 *                       PCI IDs.
 * \param devList        List of Device sections parsed from the config file.
 * \param numDevs        Number of entries in \c devList.
 * \param drvp           Pointer the driver's control structure.
 * \param foundEntities  Returned list of entity indicies associated with the
 *                       driver.
 *
 * \returns
 * The number of elements in returned in \c foundEntities on success or zero
 * on failure.
 *
 * \todo
 * This function does a bit more than short description says.  Fill in some
 * more of the details of its operation.
 *
 * \todo
 * The \c driverName parameter is redundant.  It is the same as
 * \c DriverRec::driverName.  In a future version of this function, remove
 * that parameter.
 */
int
xf86MatchPciInstances(const char *driverName, int vendorID,
                      SymTabPtr chipsets, PciChipsets * PCIchipsets,
                      GDevPtr * devList, int numDevs, DriverPtr drvp,
                      int **foundEntities)
{
    int i, j;
    struct pci_device *pPci;
    struct pci_device_iterator *iter;
    struct Inst *instances = NULL;
    int numClaimedInstances = 0;
    int allocatedInstances = 0;
    int numFound = 0;
    SymTabRec *c;
    PciChipsets *id;
    int *retEntities = NULL;

    *foundEntities = NULL;

    /* Each PCI device will contribute at least one entry.  Each device
     * section can contribute at most one entry.  The sum of the two is
     * guaranteed to be larger than the maximum possible number of entries.
     * Do this calculation and memory allocation once now to eliminate the
     * need for realloc calls inside the loop.
     */
    if (!(xf86DoConfigure && xf86DoConfigurePass1)) {
        unsigned max_entries = numDevs;

        iter = pci_slot_match_iterator_create(NULL);
        while ((pPci = pci_device_next(iter)) != NULL) {
            max_entries++;
        }

        pci_iterator_destroy(iter);
        instances = xnfallocarray(max_entries, sizeof(struct Inst));
    }

    iter = pci_slot_match_iterator_create(NULL);
    while ((pPci = pci_device_next(iter)) != NULL) {
        unsigned device_class = pPci->device_class;
        Bool foundVendor = FALSE;

        /* Convert the pre-PCI 2.0 device class for a VGA adapter to the
         * 2.0 version of the same class.
         */
        if (device_class == 0x00000101) {
            device_class = 0x00030000;
        }

        /* Find PCI devices that match the given vendor ID.  The vendor ID is
         * either specified explicitly as a parameter to the function or
         * implicitly encoded in the high bits of id->PCIid.
         *
         * The first device with a matching vendor is recorded, even if the
         * device ID doesn't match.  This is done because the Device section
         * in the xorg.conf file can over-ride the device ID.  A matching PCI
         * ID might not be found now, but after the device ID over-ride is
         * applied there /might/ be a match.
         */
        for (id = PCIchipsets; id->PCIid != -1; id++) {
            const unsigned vendor_id = ((id->PCIid & 0xFFFF0000) >> 16)
                | vendorID;
            const unsigned device_id = (id->PCIid & 0x0000FFFF);
            const unsigned match_class = 0x00030000 | id->PCIid;

            if ((vendor_id == pPci->vendor_id)
                || ((vendorID == PCI_VENDOR_GENERIC) &&
                    (match_class == device_class))) {
                if (!foundVendor && (instances != NULL)) {
                    ++allocatedInstances;
                    instances[allocatedInstances - 1].pci = pPci;
                    instances[allocatedInstances - 1].dev = NULL;
                    instances[allocatedInstances - 1].claimed = FALSE;
                    instances[allocatedInstances - 1].foundHW = FALSE;
                    instances[allocatedInstances - 1].screen = 0;
                }

                foundVendor = TRUE;

                if ((device_id == pPci->device_id)
                    || ((vendorID == PCI_VENDOR_GENERIC)
                        && (match_class == device_class))) {
                    if (instances != NULL) {
                        instances[allocatedInstances - 1].foundHW = TRUE;
                        instances[allocatedInstances - 1].chip = id->numChipset;
                    }

                    if (xf86DoConfigure && xf86DoConfigurePass1) {
                        if (xf86CheckPciSlot(pPci)) {
                            GDevPtr pGDev =
                                xf86AddBusDeviceToConfigure(drvp->driverName,
                                                            BUS_PCI, pPci, -1);

                            if (pGDev) {
                                /* After configure pass 1, chipID and chipRev
                                 * are treated as over-rides, so clobber them
                                 * here.
                                 */
                                pGDev->chipID = -1;
                                pGDev->chipRev = -1;
                            }

                            numFound++;
                        }
                    }
                    else {
                        numFound++;
                    }

                    break;
                }
            }
        }
    }

    pci_iterator_destroy(iter);

    /* In "probe only" or "configure" mode (signaled by instances being NULL),
     * our work is done.  Return the number of detected devices.
     */
    if (instances == NULL) {
        return numFound;
    }

    /*
     * This may be debatable, but if no PCI devices with a matching vendor
     * type is found, return zero now.  It is probably not desirable to
     * allow the config file to override this.
     */
    if (allocatedInstances <= 0) {
        free(instances);
        return 0;
    }

    DebugF("%s instances found: %d\n", driverName, allocatedInstances);

    /*
     * Check for devices that need duplicated instances.  This is required
     * when there is more than one screen per entity.
     *
     * XXX This currently doesn't work for cases where the BusID isn't
     * specified explicitly in the config file.
     */

    for (j = 0; j < numDevs; j++) {
        if (devList[j]->screen > 0 && devList[j]->busID && *devList[j]->busID) {
            for (i = 0; i < allocatedInstances; i++) {
                pPci = instances[i].pci;
                if (xf86ComparePciBusString(devList[j]->busID,
                                            PCI_MAKE_BUS(pPci->domain,
                                                         pPci->bus), pPci->dev,
                                            pPci->func)) {
                    allocatedInstances++;
                    instances[allocatedInstances - 1] = instances[i];
                    instances[allocatedInstances - 1].screen =
                        devList[j]->screen;
                    numFound++;
                    break;
                }
            }
        }
    }

    for (i = 0; i < allocatedInstances; i++) {
        GDevPtr dev = NULL;
        GDevPtr devBus = NULL;

        pPci = instances[i].pci;
        for (j = 0; j < numDevs; j++) {
            if (devList[j]->busID && *devList[j]->busID) {
                if (xf86ComparePciBusString(devList[j]->busID,
                                            PCI_MAKE_BUS(pPci->domain,
                                                         pPci->bus), pPci->dev,
                                            pPci->func) &&
                    devList[j]->screen == instances[i].screen) {

                    if (devBus)
                        xf86MsgVerb(X_WARNING, 0,
                                    "%s: More than one matching Device section for "
                                    "instances\n\t(BusID: %s) found: %s\n",
                                    driverName, devList[j]->busID,
                                    devList[j]->identifier);
                    else
                        devBus = devList[j];
                }
            }
            else {
                /*
                 * if device section without BusID is found
                 * only assign to it to the primary device.
                 */
                if (xf86IsPrimaryPci(pPci)) {
                    xf86Msg(X_PROBED, "Assigning device section with no busID"
                            " to primary device\n");
                    if (dev || devBus)
                        xf86MsgVerb(X_WARNING, 0,
                                    "%s: More than one matching Device section "
                                    "found: %s\n", driverName,
                                    devList[j]->identifier);
                    else
                        dev = devList[j];
                }
            }
        }
        if (devBus)
            dev = devBus;       /* busID preferred */
        if (!dev) {
            if (xf86CheckPciSlot(pPci) && pciDeviceHasBars(pPci)) {
                xf86MsgVerb(X_WARNING, 0, "%s: No matching Device section "
                            "for instance (BusID PCI:%u@%u:%u:%u) found\n",
                            driverName, pPci->bus, pPci->domain, pPci->dev,
                            pPci->func);
            }
        }
        else {
            numClaimedInstances++;
            instances[i].claimed = TRUE;
            instances[i].dev = dev;
        }
    }
    DebugF("%s instances found: %d\n", driverName, numClaimedInstances);
    /*
     * Now check that a chipset or chipID override in the device section
     * is valid.  Chipset has precedence over chipID.
     * If chipset is not valid ignore BusSlot completely.
     */
    for (i = 0; i < allocatedInstances && numClaimedInstances > 0; i++) {
        MessageType from = X_PROBED;

        if (!instances[i].claimed) {
            continue;
        }
        if (instances[i].dev->chipset) {
            for (c = chipsets; c->token >= 0; c++) {
                if (xf86NameCmp(c->name, instances[i].dev->chipset) == 0)
                    break;
            }
            if (c->token == -1) {
                instances[i].claimed = FALSE;
                numClaimedInstances--;
                xf86MsgVerb(X_WARNING, 0, "%s: Chipset \"%s\" in Device "
                            "section \"%s\" isn't valid for this driver\n",
                            driverName, instances[i].dev->chipset,
                            instances[i].dev->identifier);
            }
            else {
                instances[i].chip = c->token;

                for (id = PCIchipsets; id->numChipset >= 0; id++) {
                    if (id->numChipset == instances[i].chip)
                        break;
                }
                if (id->numChipset >= 0) {
                    xf86Msg(X_CONFIG, "Chipset override: %s\n",
                            instances[i].dev->chipset);
                    from = X_CONFIG;
                }
                else {
                    instances[i].claimed = FALSE;
                    numClaimedInstances--;
                    xf86MsgVerb(X_WARNING, 0, "%s: Chipset \"%s\" in Device "
                                "section \"%s\" isn't a valid PCI chipset\n",
                                driverName, instances[i].dev->chipset,
                                instances[i].dev->identifier);
                }
            }
        }
        else if (instances[i].dev->chipID > 0) {
            for (id = PCIchipsets; id->numChipset >= 0; id++) {
                if (id->PCIid == instances[i].dev->chipID)
                    break;
            }
            if (id->numChipset == -1) {
                instances[i].claimed = FALSE;
                numClaimedInstances--;
                xf86MsgVerb(X_WARNING, 0, "%s: ChipID 0x%04X in Device "
                            "section \"%s\" isn't valid for this driver\n",
                            driverName, instances[i].dev->chipID,
                            instances[i].dev->identifier);
            }
            else {
                instances[i].chip = id->numChipset;

                xf86Msg(X_CONFIG, "ChipID override: 0x%04X\n",
                        instances[i].dev->chipID);
                from = X_CONFIG;
            }
        }
        else if (!instances[i].foundHW) {
            /*
             * This means that there was no override and the PCI chipType
             * doesn't match one that is supported
             */
            instances[i].claimed = FALSE;
            numClaimedInstances--;
        }
        if (instances[i].claimed == TRUE) {
            for (c = chipsets; c->token >= 0; c++) {
                if (c->token == instances[i].chip)
                    break;
            }
            xf86Msg(from, "Chipset %s found\n", c->name);
        }
    }

    /*
     * Of the claimed instances, check that another driver hasn't already
     * claimed its slot.
     */
    numFound = 0;
    for (i = 0; i < allocatedInstances && numClaimedInstances > 0; i++) {
        if (!instances[i].claimed)
            continue;
        pPci = instances[i].pci;

        /*
         * Allow the same entity to be used more than once for devices with
         * multiple screens per entity.  This assumes implicitly that there
         * will be a screen == 0 instance.
         *
         * XXX Need to make sure that two different drivers don't claim
         * the same screen > 0 instance.
         */
        if (instances[i].screen == 0 && !xf86CheckPciSlot(pPci))
            continue;

        DebugF("%s: card at %d:%d:%d is claimed by a Device section\n",
               driverName, pPci->bus, pPci->dev, pPci->func);

        /* Allocate an entry in the lists to be returned */
        numFound++;
        retEntities = xnfreallocarray(retEntities, numFound, sizeof(int));
        retEntities[numFound - 1] = xf86ClaimPciSlot(pPci, drvp,
                                                     instances[i].chip,
                                                     instances[i].dev,
                                                     instances[i].dev->active);
        if (retEntities[numFound - 1] == -1 && instances[i].screen > 0) {
            for (j = 0; j < xf86NumEntities; j++) {
                EntityPtr pEnt = xf86Entities[j];

                if (pEnt->bus.type != BUS_PCI)
                    continue;
                if (pEnt->bus.id.pci == pPci) {
                    retEntities[numFound - 1] = j;
                    xf86AddDevToEntity(j, instances[i].dev);
                    break;
                }
            }
        }
    }
    free(instances);
    if (numFound > 0) {
        *foundEntities = retEntities;
    }

    return numFound;
}

/*
 * xf86ConfigPciEntityInactive() -- This function can be used
 * to configure an inactive entity as well as to reconfigure an
 * previously active entity inactive. If the entity has been
 * assigned to a screen before it will be removed. If p_chip is
 * non-NULL all static resources listed there will be registered.
 */
static void
xf86ConfigPciEntityInactive(EntityInfoPtr pEnt, PciChipsets * p_chip,
                            EntityProc init, EntityProc enter,
                            EntityProc leave, void *private)
{
    ScrnInfoPtr pScrn;

    if ((pScrn = xf86FindScreenForEntity(pEnt->index)))
        xf86RemoveEntityFromScreen(pScrn, pEnt->index);
}

ScrnInfoPtr
xf86ConfigPciEntity(ScrnInfoPtr pScrn, int scrnFlag, int entityIndex,
                    PciChipsets * p_chip, void *dummy, EntityProc init,
                    EntityProc enter, EntityProc leave, void *private)
{
    EntityInfoPtr pEnt = xf86GetEntityInfo(entityIndex);

    if (dummy || init || enter || leave)
        FatalError("Legacy entity access functions are unsupported\n");

    if (!pEnt)
        return pScrn;

    if (!(pEnt->location.type == BUS_PCI)
        || !xf86GetPciInfoForEntity(entityIndex)) {
        free(pEnt);
        return pScrn;
    }
    if (!pEnt->active) {
        xf86ConfigPciEntityInactive(pEnt, p_chip, init, enter, leave, private);
        free(pEnt);
        return pScrn;
    }

    if (!pScrn)
        pScrn = xf86AllocateScreen(pEnt->driver, scrnFlag);
    if (xf86IsEntitySharable(entityIndex)) {
        xf86SetEntityShared(entityIndex);
    }
    xf86AddEntityToScreen(pScrn, entityIndex);
    if (xf86IsEntityShared(entityIndex)) {
        return pScrn;
    }
    free(pEnt);

    return pScrn;
}

void
xf86VideoPtrToDriverList(struct pci_device *dev, XF86MatchedDrivers *md)
{
    int i;

    /* Add more entries here if we ever return more than 4 drivers for
       any device */
    const char *driverList[5] = { NULL, NULL, NULL, NULL, NULL };

    switch (dev->vendor_id) {
        /* AMD Geode LX */
    case 0x1022:
        if (dev->device_id == 0x2081)
            driverList[0] = "geode";
        break;
        /* older Geode products acquired by AMD still carry an NSC vendor_id */
    case 0x100b:
        if (dev->device_id == 0x0030) {
            /* NSC Geode GX2 specifically */
            driverList[0] = "geode";
            /* GX2 support started its life in the NSC tree and was later
               forked by AMD for GEODE so we keep it as a backup */
            driverList[1] = "nsc";
        }
        else
            /* other NSC variant e.g. 0x0104 (SC1400), 0x0504 (SCx200) */
            driverList[0] = "nsc";
        break;
        /* Cyrix Geode GX1 */
    case 0x1078:
        if (dev->device_id == 0x0104)
            driverList[0] = "cyrix";
        break;
    case 0x1142:
        driverList[0] = "apm";
        break;
    case 0xedd8:
        driverList[0] = "ark";
        break;
    case 0x1a03:
        driverList[0] = "ast";
        break;
    case 0x1002:
        driverList[0] = "ati";
        break;
    case 0x102c:
        driverList[0] = "chips";
        break;
    case 0x1013:
        driverList[0] = "cirrus";
        break;
    case 0x3d3d:
        driverList[0] = "glint";
        break;
    case 0x105d:
        driverList[0] = "i128";
        break;
    case 0x8086:
	switch (dev->device_id)
	{
		/* Intel i740 */
		case 0x00d1:
		case 0x7800:
			driverList[0] = "i740";
			break;
		/* GMA500/Poulsbo */
		case 0x8108:
		case 0x8109:
			/* Try psb driver on Poulsbo - if available */
			driverList[0] = "psb";
			driverList[1] = "psb_drv";
			break;
		/* GMA600/Oaktrail */
		case 0x4100:
		case 0x4101:
		case 0x4102:
		case 0x4103:
		case 0x4104:
		case 0x4105:
		case 0x4106:
		case 0x4107:
		/* Atom E620/Oaktrail */
		case 0x4108:
		/* Medfield */
		case 0x0130:
		case 0x0131:
		case 0x0132:
		case 0x0133:
		case 0x0134:
		case 0x0135:
		case 0x0136:
		case 0x0137:
		/* GMA 3600/CDV */
		case 0x0be0:
		case 0x0be1:
		case 0x0be2:
		case 0x0be3:
		case 0x0be4:
		case 0x0be5:
		case 0x0be6:
		case 0x0be7:
		case 0x0be8:
		case 0x0be9:
		case 0x0bea:
		case 0x0beb:
		case 0x0bec:
		case 0x0bed:
		case 0x0bee:
		case 0x0bef:
			/* Use fbdev/vesa driver on Oaktrail, Medfield, CDV */
			break;
		default:
			driverList[0] = "intel";
			break;
        }
        break;
    case 0x102b:
        driverList[0] = "mga";
        break;
    case 0x10c8:
        driverList[0] = "neomagic";
        break;
    case 0x10de:
    case 0x12d2:
    {
        int idx = 0;

#if defined(__linux__) || defined(__NetBSD__)
        driverList[idx++] = "nouveau";
#endif
        driverList[idx++] = "nv";
        break;
    }
    case 0x1106:
        driverList[0] = "openchrome";
        break;
    case 0x1b36:
        driverList[0] = "qxl";
        break;
    case 0x1163:
        driverList[0] = "rendition";
        break;
    case 0x5333:
        switch (dev->device_id) {
        case 0x88d0:
        case 0x88d1:
        case 0x88f0:
        case 0x8811:
        case 0x8812:
        case 0x8814:
        case 0x8901:
            driverList[0] = "s3";
            break;
        case 0x5631:
        case 0x883d:
        case 0x8a01:
        case 0x8a10:
        case 0x8c01:
        case 0x8c03:
        case 0x8904:
        case 0x8a13:
            driverList[0] = "s3virge";
            break;
        default:
            driverList[0] = "savage";
            break;
        }
        break;
    case 0x1039:
        driverList[0] = "sis";
        break;
    case 0x126f:
        driverList[0] = "siliconmotion";
        break;
    case 0x121a:
        if (dev->device_id < 0x0003)
            driverList[0] = "voodoo";
        else
            driverList[0] = "tdfx";
        break;
    case 0x1011:
        driverList[0] = "tga";
        break;
    case 0x1023:
        driverList[0] = "trident";
        break;
    case 0x100c:
        driverList[0] = "tseng";
        break;
    case 0x80ee:
        driverList[0] = "vboxvideo";
        break;
    case 0x15ad:
        driverList[0] = "vmware";
        break;
    case 0x18ca:
        if (dev->device_id == 0x47)
            driverList[0] = "xgixp";
        else
            driverList[0] = "xgi";
        break;
    default:
        break;
    }
    for (i = 0; driverList[i] != NULL; i++) {
        xf86AddMatchedDriver(md, driverList[i]);
    }
}

#ifdef __linux__
static int
xchomp(char *line)
{
    size_t len = 0;

    if (!line) {
        return 1;
    }

    len = strlen(line);
    if (line[len - 1] == '\n' && len > 0) {
        line[len - 1] = '\0';
    }
    return 0;
}

/* This function is used to provide a workaround for binary drivers that
 * don't export their PCI ID's properly. If distros don't end up using this
 * feature it can and should be removed because the symbol-based resolution
 * scheme should be the primary one */
void
xf86MatchDriverFromFiles(uint16_t match_vendor, uint16_t match_chip,
                         XF86MatchedDrivers *md)
{
    DIR *idsdir;
    FILE *fp;
    struct dirent *direntry;
    char *line = NULL, *tmpMatch;
    size_t len;
    ssize_t read;
    char path_name[512], vendor_str[5], chip_str[5];
    uint16_t vendor, chip;
    int j;

    idsdir = opendir(PCI_TXT_IDS_PATH);
    if (!idsdir)
        return;

    xf86Msg(X_INFO,
            "Scanning %s directory for additional PCI ID's supported by the drivers\n",
            PCI_TXT_IDS_PATH);
    direntry = readdir(idsdir);
    /* Read the directory */
    while (direntry) {
        if (direntry->d_name[0] == '.') {
            direntry = readdir(idsdir);
            continue;
        }
        len = strlen(direntry->d_name);
        /* A tiny bit of sanity checking. We should probably do better */
        if (strncmp(&(direntry->d_name[len - 4]), ".ids", 4) == 0) {
            /* We need the full path name to open the file */
            snprintf(path_name, sizeof(path_name), "%s/%s",
                     PCI_TXT_IDS_PATH, direntry->d_name);
            fp = fopen(path_name, "r");
            if (fp == NULL) {
                xf86Msg(X_ERROR, "Could not open %s for reading. Exiting.\n",
                        path_name);
                goto end;
            }
            /* Read the file */
#ifdef __GLIBC__
            while ((read = getline(&line, &len, fp)) != -1) {
#else
            while ((line = fgetln(fp, &len)) != (char *) NULL) {
#endif                          /* __GLIBC __ */
                xchomp(line);
                if (isdigit(line[0])) {
                    strlcpy(vendor_str, line, sizeof(vendor_str));
                    vendor = (int) strtol(vendor_str, NULL, 16);
                    if ((strlen(&line[4])) == 0) {
                        chip_str[0] = '\0';
                        chip = -1;
                    }
                    else {
                        /* Handle trailing whitespace */
                        if (isspace(line[4])) {
                            chip_str[0] = '\0';
                            chip = -1;
                        }
                        else {
                            /* Ok, it's a real ID */
                            strlcpy(chip_str, &line[4], sizeof(chip_str));
                            chip = (int) strtol(chip_str, NULL, 16);
                        }
                    }
                    if (vendor == match_vendor && chip == match_chip) {
                        tmpMatch =
                            (char *) malloc(sizeof(char) *
                                            strlen(direntry->d_name) - 3);
                        if (!tmpMatch) {
                            xf86Msg(X_ERROR,
                                    "Could not allocate space for the module name. Exiting.\n");
                            goto end;
                        }
                        /* hack off the .ids suffix. This should guard
                         * against other problems, but it will end up
                         * taking off anything after the first '.' */
                        for (j = 0; j < (strlen(direntry->d_name) - 3); j++) {
                            if (direntry->d_name[j] == '.') {
                                tmpMatch[j] = '\0';
                                break;
                            }
                            else {
                                tmpMatch[j] = direntry->d_name[j];
                            }
                        }
                        xf86AddMatchedDriver(md, tmpMatch);
                        xf86Msg(X_INFO, "Matched %s from file name %s\n",
                                tmpMatch, direntry->d_name);
                        free(tmpMatch);
                    }
                }
                else {
                    /* TODO Handle driver overrides here */
                }
            }
            fclose(fp);
        }
        direntry = readdir(idsdir);
    }
 end:
    free(line);
    closedir(idsdir);
}
#endif                          /* __linux__ */

void
xf86PciMatchDriver(XF86MatchedDrivers *md)
{
    struct pci_device *info = NULL;
    struct pci_device_iterator *iter;

    /* Find the primary device, and get some information about it. */
    iter = pci_slot_match_iterator_create(NULL);
    while ((info = pci_device_next(iter)) != NULL) {
        if (xf86IsPrimaryPci(info)) {
            break;
        }
    }

    pci_iterator_destroy(iter);
#ifdef __linux__
    if (info)
        xf86MatchDriverFromFiles(info->vendor_id, info->device_id, md);
#endif

    if (info != NULL) {
        xf86VideoPtrToDriverList(info, md);
    }
}

Bool
xf86PciConfigure(void *busData, struct pci_device *pDev)
{
    struct pci_device *pVideo = NULL;

    pVideo = (struct pci_device *) busData;
    if (pDev &&
        (pDev->domain == pVideo->domain) &&
        (pDev->bus == pVideo->bus) &&
        (pDev->dev == pVideo->dev) && (pDev->func == pVideo->func))
        return 0;

    return 1;
}

void
xf86PciConfigureNewDev(void *busData, struct pci_device *pVideo,
                       GDevRec * GDev, int *chipset)
{
    char busnum[8];
    char *tmp;

    pVideo = (struct pci_device *) busData;

    snprintf(busnum, sizeof(busnum), "%d", pVideo->bus);

    XNFasprintf(&tmp, "PCI:%s:%d:%d",
                busnum, pVideo->dev, pVideo->func);
    GDev->busID = tmp;

    GDev->chipID = pVideo->device_id;
    GDev->chipRev = pVideo->revision;

    if (*chipset < 0)
        *chipset = (pVideo->vendor_id << 16) | pVideo->device_id;
}

char *
DRICreatePCIBusID(const struct pci_device *dev)
{
    char *busID;

    if (asprintf(&busID, "pci:%04x:%02x:%02x.%d",
                 dev->domain, dev->bus, dev->dev, dev->func) == -1)
        return NULL;

    return busID;
}
