/*
 * 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 "os.h"
#include "xf86.h"
#include "xf86Priv.h"

/* Bus-specific headers */

#include "xf86Bus.h"

#define XF86_OS_PRIVS
#include "xf86_OSproc.h"
#include "xf86VGAarbiter.h"

/* Entity data */
EntityPtr *xf86Entities = NULL;	/* Bus slots claimed by drivers */
int xf86NumEntities = 0;
static int xf86EntityPrivateCount = 0;

BusRec primaryBus = { BUS_NONE, { 0 } };

/**
 * Call the driver's correct probe function.
 *
 * If the driver implements the \c DriverRec::PciProbe entry-point and an
 * appropriate PCI device (with matching Device section in the xorg.conf file)
 * is found, it is called.  If \c DriverRec::PciProbe or no devices can be
 * successfully probed with it (e.g., only non-PCI devices are available),
 * the driver's \c DriverRec::Probe function is called.
 *
 * \param drv   Driver to probe
 *
 * \return
 * If a device can be successfully probed by the driver, \c TRUE is
 * returned.  Otherwise, \c FALSE is returned.
 */
Bool
xf86CallDriverProbe( DriverPtr drv, Bool detect_only )
{
    Bool     foundScreen = FALSE;

    if (drv->PciProbe != NULL) {
        if (xf86DoConfigure && xf86DoConfigurePass1) {
            assert(detect_only);
            foundScreen = xf86PciAddMatchingDev(drv);
        }
        else {
            assert(! detect_only);
            foundScreen = xf86PciProbeDev(drv);
        }
    }

    if (!foundScreen && (drv->Probe != NULL)) {
        xf86Msg( X_WARNING, "Falling back to old probe method for %s\n",
                             drv->driverName);
        foundScreen = (*drv->Probe)(drv, (detect_only) ? PROBE_DETECT
                                    : PROBE_DEFAULT);
    }

    return foundScreen;
}

/**
 * @return TRUE if all buses are configured and set up correctly and FALSE
 * otherwise.
 */
Bool
xf86BusConfig(void)
{
    screenLayoutPtr layout;
    int i, j;

    /* Enable full I/O access */
    if (xorgHWAccess)
        xorgHWAccess = xf86EnableIO();

    /*
     * Now call each of the Probe functions.  Each successful probe will
     * result in an extra entry added to the xf86Screens[] list for each
     * instance of the hardware found.
     */
    for (i = 0; i < xf86NumDrivers; i++) {
        xorgHWFlags flags;
        if (!xorgHWAccess) {
            if (!xf86DriverList[i]->driverFunc
            || !xf86DriverList[i]->driverFunc(NULL,
                             GET_REQUIRED_HW_INTERFACES,
                              &flags)
            || NEED_IO_ENABLED(flags))
            continue;
        }

        xf86CallDriverProbe(xf86DriverList[i], FALSE);
    }

    /* If nothing was detected, return now */
    if (xf86NumScreens == 0) {
        xf86Msg(X_ERROR, "No devices detected.\n");
        return FALSE;
    }

    xf86VGAarbiterInit();

    /*
     * Match up the screens found by the probes against those specified
     * in the config file.  Remove the ones that won't be used.  Sort
     * them in the order specified.
     *
     * What is the best way to do this?
     *
     * For now, go through the screens allocated by the probes, and
     * look for screen config entry which refers to the same device
     * section as picked out by the probe.
     *
     */
    for (i = 0; i < xf86NumScreens; i++) {
        for (layout = xf86ConfigLayout.screens; layout->screen != NULL;
             layout++) {
            Bool found = FALSE;
            for (j = 0; j < xf86Screens[i]->numEntities; j++) {

                GDevPtr dev = xf86GetDevFromEntity(
                                xf86Screens[i]->entityList[j],
                                xf86Screens[i]->entityInstanceList[j]);
                if (dev == layout->screen->device) {
                    /* A match has been found */
                    xf86Screens[i]->confScreen = layout->screen;
                    found = TRUE;
                    break;
                }
            }
            if (found) break;
        }
        if (layout->screen == NULL) {
            /* No match found */
            xf86Msg(X_ERROR,
            "Screen %d deleted because of no matching config section.\n", i);
            xf86DeleteScreen(i--, 0);
        }
    }

    /* If no screens left, return now.  */
    if (xf86NumScreens == 0) {
        xf86Msg(X_ERROR,
        "Device(s) detected, but none match those in the config file.\n");
        return FALSE;
    }

    return TRUE;
}

/*
 * Call the bus probes relevant to the architecture.
 *
 * The only one available so far is for PCI and SBUS.
 */

void
xf86BusProbe(void)
{
    xf86PciProbe();
#if (defined(__sparc__) || defined(__sparc)) && !defined(__OpenBSD__)
    xf86SbusProbe();
#endif
}

/*
 * Determine what bus type the busID string represents.  The start of the
 * bus-dependent part of the string is returned as retID.
 */

BusType
StringToBusType(const char* busID, const char **retID)
{
    char *p, *s;
    BusType ret = BUS_NONE;

    /* If no type field, Default to PCI */
    if (isdigit(busID[0])) {
	if (retID)
	    *retID = busID;
	return BUS_PCI;
    }

    s = xstrdup(busID);
    p = strtok(s, ":");
    if (p == NULL || *p == 0) {
	free(s);
	return BUS_NONE;
    }
    if (!xf86NameCmp(p, "pci") || !xf86NameCmp(p, "agp"))
	ret = BUS_PCI; 
    if (!xf86NameCmp(p, "sbus"))
	ret = BUS_SBUS;
    if (ret != BUS_NONE)
	if (retID)
	    *retID = busID + strlen(p) + 1;
    free(s);
    return ret;
}

int
xf86AllocateEntity(void)
{
    xf86NumEntities++;
    xf86Entities = xnfrealloc(xf86Entities,
			      sizeof(EntityPtr) * xf86NumEntities);
    xf86Entities[xf86NumEntities - 1] = xnfcalloc(1,sizeof(EntityRec));
    xf86Entities[xf86NumEntities - 1]->entityPrivates =
               xnfcalloc(sizeof(DevUnion) * xf86EntityPrivateCount, 1);
    return xf86NumEntities - 1;
}

Bool
xf86IsEntityPrimary(int entityIndex)
{
    EntityPtr pEnt = xf86Entities[entityIndex];
    
    if (primaryBus.type != pEnt->bus.type) return FALSE;

    switch (pEnt->bus.type) {
    case BUS_PCI:
	return pEnt->bus.id.pci == primaryBus.id.pci;
    case BUS_SBUS:
	return pEnt->bus.id.sbus.fbNum == primaryBus.id.sbus.fbNum;
    default:
	return FALSE;
    }
}
	
Bool
xf86SetEntityFuncs(int entityIndex, EntityProc init, EntityProc enter,
		   EntityProc leave, pointer private)
{
    if (entityIndex >= xf86NumEntities)
	return FALSE;
    xf86Entities[entityIndex]->entityInit = init;
    xf86Entities[entityIndex]->entityEnter = enter;
    xf86Entities[entityIndex]->entityLeave = leave;
    xf86Entities[entityIndex]->private = private;
    return TRUE;
}

Bool
xf86DriverHasEntities(DriverPtr drvp)
{
    int i;
    for (i = 0; i < xf86NumEntities; i++) {
	if (xf86Entities[i]->driver == drvp) 
	    return TRUE;
    }
    return FALSE;
}

void
xf86AddEntityToScreen(ScrnInfoPtr pScrn, int entityIndex)
{
    if (entityIndex == -1)
	return;
    if (xf86Entities[entityIndex]->inUse &&
	!(xf86Entities[entityIndex]->entityProp & IS_SHARED_ACCEL)) {
	ErrorF("Requested Entity already in use!\n");
	return;
    }

    pScrn->numEntities++;
    pScrn->entityList = xnfrealloc(pScrn->entityList,
				    pScrn->numEntities * sizeof(int));
    pScrn->entityList[pScrn->numEntities - 1] = entityIndex;
    xf86Entities[entityIndex]->inUse = TRUE;
    pScrn->entityInstanceList = xnfrealloc(pScrn->entityInstanceList,
				    pScrn->numEntities * sizeof(int));
    pScrn->entityInstanceList[pScrn->numEntities - 1] = 0;
    pScrn->domainIOBase = xf86Entities[entityIndex]->domainIO;
}

void
xf86SetEntityInstanceForScreen(ScrnInfoPtr pScrn, int entityIndex, int instance)
{
    int i;

    if (entityIndex == -1 || entityIndex >= xf86NumEntities)
	return;

    for (i = 0; i < pScrn->numEntities; i++) {
	if (pScrn->entityList[i] == entityIndex) {
	    pScrn->entityInstanceList[i] = instance;
	    break;
	}
    }
}

/*
 * XXX  This needs to be updated for the case where a single entity may have
 * instances associated with more than one screen.
 */
ScrnInfoPtr
xf86FindScreenForEntity(int entityIndex)
{
    int i,j;

    if (entityIndex == -1) return NULL;
    
    if (xf86Screens) {
	for (i = 0; i < xf86NumScreens; i++) {
	    for (j = 0; j < xf86Screens[i]->numEntities; j++) {
		if ( xf86Screens[i]->entityList[j] == entityIndex )
		    return xf86Screens[i];
	    }
	}
    }
    return NULL;
}

void
xf86RemoveEntityFromScreen(ScrnInfoPtr pScrn, int entityIndex)
{
    int i;
    
    for (i = 0; i < pScrn->numEntities; i++) {
	if (pScrn->entityList[i] == entityIndex) {
	    for (i++; i < pScrn->numEntities; i++)
		pScrn->entityList[i-1] = pScrn->entityList[i];
	    pScrn->numEntities--;
	    xf86Entities[entityIndex]->inUse = FALSE;
	    break;
	}
    }
}

/*
 * xf86ClearEntitiesForScreen() - called when a screen is deleted
 * to mark it's entities unused. Called by xf86DeleteScreen().
 */
void
xf86ClearEntityListForScreen(int scrnIndex)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    int i, entityIndex;
    
    if (pScrn->entityList == NULL || pScrn->numEntities == 0) return;
	
    for (i = 0; i < pScrn->numEntities; i++) {
	entityIndex = pScrn->entityList[i];
	xf86Entities[entityIndex]->inUse = FALSE;
	/* disable resource: call the disable function */
    }
    free(pScrn->entityList);
    free(pScrn->entityInstanceList);
    pScrn->entityList = NULL;
    pScrn->entityInstanceList = NULL;
}

/*
 * Add an extra device section (GDevPtr) to an entity.
 */

void
xf86AddDevToEntity(int entityIndex, GDevPtr dev)
{
    EntityPtr pEnt;
    
    if (entityIndex >= xf86NumEntities)
	return;
    
    pEnt = xf86Entities[entityIndex];
    pEnt->numInstances++;
    pEnt->devices = xnfrealloc(pEnt->devices,
				pEnt->numInstances * sizeof(GDevPtr));
    pEnt->devices[pEnt->numInstances - 1] = dev;
    dev->claimed = TRUE;
}

/*
 * xf86GetEntityInfo() -- This function hands information from the
 * EntityRec struct to the drivers. The EntityRec structure itself
 * remains invisible to the driver.
 */
EntityInfoPtr
xf86GetEntityInfo(int entityIndex)
{
    EntityInfoPtr pEnt;
    int i;
    
    if (entityIndex == -1)
	return NULL;

    if (entityIndex >= xf86NumEntities)
	return NULL;
    
    pEnt = xnfcalloc(1,sizeof(EntityInfoRec));
    pEnt->index = entityIndex;
    pEnt->location = xf86Entities[entityIndex]->bus;
    pEnt->active = xf86Entities[entityIndex]->active;
    pEnt->chipset = xf86Entities[entityIndex]->chipset;
    pEnt->driver = xf86Entities[entityIndex]->driver;
    if ( (xf86Entities[entityIndex]->devices) &&
         (xf86Entities[entityIndex]->devices[0]) ) {
	for (i = 0; i < xf86Entities[entityIndex]->numInstances; i++)
	    if (xf86Entities[entityIndex]->devices[i]->screen == 0)
	        break;
	pEnt->device = xf86Entities[entityIndex]->devices[i];
    } else
	pEnt->device = NULL;
    
    return pEnt;
}

int
xf86GetNumEntityInstances(int entityIndex)
{
    if (entityIndex >= xf86NumEntities)
	return -1;
    
    return xf86Entities[entityIndex]->numInstances;
}

GDevPtr
xf86GetDevFromEntity(int entityIndex, int instance)
{
    int i;
  
    /* We might not use AddDevtoEntity */
    if ( (!xf86Entities[entityIndex]->devices) ||
         (!xf86Entities[entityIndex]->devices[0]) ) 
	return NULL;

    if (entityIndex >= xf86NumEntities ||
	instance >= xf86Entities[entityIndex]->numInstances)
	return NULL;
    
    for (i = 0; i < xf86Entities[entityIndex]->numInstances; i++)
	if (xf86Entities[entityIndex]->devices[i]->screen == instance)
	    break;
    return xf86Entities[entityIndex]->devices[i];
}

/*
 * xf86AccessEnter() -- gets called to save the text mode VGA IO 
 * resources when reentering the server after a VT switch.
 */
void
xf86AccessEnter(void)
{
    int i;

    for (i = 0; i < xf86NumEntities; i++)
        if (xf86Entities[i]->entityEnter)
		xf86Entities[i]->entityEnter(i,xf86Entities[i]->private);
}

void
xf86AccessLeave(void)
{
    int i;

    for (i = 0; i < xf86NumEntities; i++)
        if (xf86Entities[i]->entityLeave)
		xf86Entities[i]->entityLeave(i,xf86Entities[i]->private);
}

/*
 * xf86PostProbe() -- Allocate all non conflicting resources
 * This function gets called by xf86Init().
 */
void
xf86PostProbe(void)
{
    int i;

    if (fbSlotClaimed && (pciSlotClaimed
#if (defined(__sparc__) || defined(__sparc)) && !defined(__OpenBSD__)
	    || sbusSlotClaimed
#endif
	    ))
	    FatalError("Cannot run in framebuffer mode. Please specify busIDs "
		       "       for all framebuffer devices\n");

    for (i = 0; i < xf86NumEntities; i++)
        if (xf86Entities[i]->entityInit)
	    xf86Entities[i]->entityInit(i,xf86Entities[i]->private);
}

int
xf86GetLastScrnFlag(int entityIndex)
{
    if(entityIndex < xf86NumEntities) {
        return xf86Entities[entityIndex]->lastScrnFlag;
    } else {
        return -1;
    }
}

void
xf86SetLastScrnFlag(int entityIndex, int scrnIndex)
{
    if(entityIndex < xf86NumEntities) {
        xf86Entities[entityIndex]->lastScrnFlag = scrnIndex;
    }
}

Bool
xf86IsEntityShared(int entityIndex)
{
    if(entityIndex < xf86NumEntities) {
        if(xf86Entities[entityIndex]->entityProp & IS_SHARED_ACCEL) {
	    return TRUE;
	}
    }
    return FALSE;
}

void
xf86SetEntityShared(int entityIndex)
{
    if(entityIndex < xf86NumEntities) {
        xf86Entities[entityIndex]->entityProp |= IS_SHARED_ACCEL;
    }
}

Bool
xf86IsEntitySharable(int entityIndex)
{
    if(entityIndex < xf86NumEntities) {
        if(xf86Entities[entityIndex]->entityProp & ACCEL_IS_SHARABLE) {
	    return TRUE;
	}
    }
    return FALSE;
}

void
xf86SetEntitySharable(int entityIndex)
{
    if(entityIndex < xf86NumEntities) {
        xf86Entities[entityIndex]->entityProp |= ACCEL_IS_SHARABLE;
    }
}

Bool
xf86IsPrimInitDone(int entityIndex)
{
    if(entityIndex < xf86NumEntities) {
        if(xf86Entities[entityIndex]->entityProp & SA_PRIM_INIT_DONE) {
	    return TRUE;
	}
    }
    return FALSE;
}

void
xf86SetPrimInitDone(int entityIndex)
{
    if(entityIndex < xf86NumEntities) {
        xf86Entities[entityIndex]->entityProp |= SA_PRIM_INIT_DONE;
    }
}

void
xf86ClearPrimInitDone(int entityIndex)
{
    if(entityIndex < xf86NumEntities) {
        xf86Entities[entityIndex]->entityProp &= ~SA_PRIM_INIT_DONE;
    }
}


/*
 * Allocate a private in the entities.
 */

int
xf86AllocateEntityPrivateIndex(void)
{
    int idx, i;
    EntityPtr pEnt;
    DevUnion *nprivs;

    idx = xf86EntityPrivateCount++;
    for (i = 0; i < xf86NumEntities; i++) {
	pEnt = xf86Entities[i];
	nprivs = xnfrealloc(pEnt->entityPrivates,
			    xf86EntityPrivateCount * sizeof(DevUnion));
	/* Zero the new private */
	memset(&nprivs[idx], 0, sizeof(DevUnion));
	pEnt->entityPrivates = nprivs;
    }
    return idx;
}

DevUnion *
xf86GetEntityPrivate(int entityIndex, int privIndex)
{
    if (entityIndex >= xf86NumEntities || privIndex >= xf86EntityPrivateCount)
	return NULL;

    return &(xf86Entities[entityIndex]->entityPrivates[privIndex]);
}