Save in a few special cases, _X_EXPORT should not be used in C source files. Instead, it should be used in headers, and the proper C source include that header. Some special cases are symbols that need to be shared between modules, but not expected to be used by external drivers, and symbols that are accessible via LoaderSymbol/dlopen. This patch also adds conditionally some new sdk header files, depending on extensions enabled. These files were added to match pattern for other extensions/modules, that is, have the headers "deciding" symbol visibility in the sdk. These headers are: o Xext/panoramiXsrv.h, Xext/panoramiX.h o fbpict.h (unconditionally) o vidmodeproc.h o mioverlay.h (unconditionally, used only by xaa) o xfixes.h (unconditionally, symbols required by dri2) LoaderSymbol and similar functions now don't have different prototypes, in loaderProcs.h and xf86Module.h, so that both headers can be included, without the need of defining IN_LOADER. xf86NewInputDevice() device prototype readded to xf86Xinput.h, but not exported (and with a comment about it).
2591 lines
62 KiB
C
2591 lines
62 KiB
C
/*
|
|
* 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"
|
|
#include "xf86Resources.h"
|
|
|
|
/* Bus-specific headers */
|
|
|
|
#include "xf86Bus.h"
|
|
|
|
#define XF86_OS_PRIVS
|
|
#define NEED_OS_RAC_PROTOS
|
|
#include "xf86_OSproc.h"
|
|
|
|
#include "xf86RAC.h"
|
|
#include "Pci.h"
|
|
|
|
/* Entity data */
|
|
EntityPtr *xf86Entities = NULL; /* Bus slots claimed by drivers */
|
|
int xf86NumEntities = 0;
|
|
static int xf86EntityPrivateCount = 0;
|
|
BusAccPtr xf86BusAccInfo = NULL;
|
|
|
|
static void
|
|
noopEnableDisable(void *arg) { }
|
|
|
|
xf86AccessRec AccessNULL = { noopEnableDisable, noopEnableDisable, NULL };
|
|
|
|
xf86CurrentAccessRec xf86CurrentAccess = {NULL,NULL};
|
|
|
|
BusRec primaryBus = { BUS_NONE, {{0}}};
|
|
|
|
static Bool xf86ResAccessEnter = FALSE;
|
|
|
|
|
|
/* resource lists */
|
|
resPtr Acc = NULL;
|
|
|
|
/* predefined special resources */
|
|
resRange resVgaExclusive[] = {_VGA_EXCLUSIVE, _END};
|
|
resRange resVgaShared[] = {_VGA_SHARED, _END};
|
|
resRange resVgaMemShared[] = {_VGA_SHARED_MEM,_END};
|
|
resRange resVgaIoShared[] = {_VGA_SHARED_IO,_END};
|
|
resRange resVgaUnusedExclusive[] = {_VGA_EXCLUSIVE_UNUSED, _END};
|
|
resRange resVgaUnusedShared[] = {_VGA_SHARED_UNUSED, _END};
|
|
resRange resVgaSparseExclusive[] = {_VGA_EXCLUSIVE_SPARSE, _END};
|
|
resRange resVgaSparseShared[] = {_VGA_SHARED_SPARSE, _END};
|
|
resRange res8514Exclusive[] = {_8514_EXCLUSIVE, _END};
|
|
resRange res8514Shared[] = {_8514_SHARED, _END};
|
|
|
|
/* Flag: do we need RAC ? */
|
|
static Bool needRAC = FALSE;
|
|
static Bool doFramebufferMode = FALSE;
|
|
|
|
/* state change notification callback list */
|
|
static StateChangeNotificationPtr StateChangeNotificationList;
|
|
static void notifyStateChange(xf86NotifyState state);
|
|
|
|
/*
|
|
* 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) {
|
|
xfree(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;
|
|
xfree(s);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Entity related code.
|
|
*/
|
|
|
|
void
|
|
xf86EntityInit(void)
|
|
{
|
|
int i;
|
|
xf86AccessPtr pacc;
|
|
|
|
for (i = 0; i < xf86NumEntities; i++)
|
|
if (xf86Entities[i]->entityInit) {
|
|
if (xf86Entities[i]->access->busAcc)
|
|
((BusAccPtr)xf86Entities[i]->access->busAcc)->set_f
|
|
(xf86Entities[i]->access->busAcc);
|
|
pacc = xf86Entities[i]->access->fallback;
|
|
pacc->AccessEnable(pacc->arg);
|
|
xf86Entities[i]->entityInit(i,xf86Entities[i]->private);
|
|
pacc->AccessDisable(pacc->arg);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static void
|
|
EntityEnter(void)
|
|
{
|
|
int i;
|
|
xf86AccessPtr pacc;
|
|
|
|
for (i = 0; i < xf86NumEntities; i++)
|
|
if (xf86Entities[i]->entityEnter) {
|
|
if (xf86Entities[i]->access->busAcc)
|
|
((BusAccPtr)xf86Entities[i]->access->busAcc)->set_f
|
|
(xf86Entities[i]->access->busAcc);
|
|
pacc = xf86Entities[i]->access->fallback;
|
|
pacc->AccessEnable(pacc->arg);
|
|
xf86Entities[i]->entityEnter(i,xf86Entities[i]->private);
|
|
pacc->AccessDisable(pacc->arg);
|
|
}
|
|
}
|
|
|
|
static void
|
|
EntityLeave(void)
|
|
{
|
|
int i;
|
|
xf86AccessPtr pacc;
|
|
|
|
for (i = 0; i < xf86NumEntities; i++)
|
|
if (xf86Entities[i]->entityLeave) {
|
|
if (xf86Entities[i]->access->busAcc)
|
|
((BusAccPtr)xf86Entities[i]->access->busAcc)->set_f
|
|
(xf86Entities[i]->access->busAcc);
|
|
pacc = xf86Entities[i]->access->fallback;
|
|
pacc->AccessEnable(pacc->arg);
|
|
xf86Entities[i]->entityLeave(i,xf86Entities[i]->private);
|
|
pacc->AccessDisable(pacc->arg);
|
|
}
|
|
}
|
|
|
|
Bool
|
|
xf86IsEntityPrimary(int entityIndex)
|
|
{
|
|
EntityPtr pEnt = xf86Entities[entityIndex];
|
|
|
|
if (primaryBus.type != pEnt->busType) return FALSE;
|
|
|
|
switch (pEnt->busType) {
|
|
case BUS_PCI:
|
|
return (pEnt->bus.id.pci == primaryBus.id.pci);
|
|
case BUS_SBUS:
|
|
return (pEnt->sbusBusId.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]->access->next = pScrn->access;
|
|
pScrn->access = xf86Entities[entityIndex]->access;
|
|
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;
|
|
EntityAccessPtr *ptr = (EntityAccessPtr *)&pScrn->access;
|
|
EntityAccessPtr peacc;
|
|
|
|
for (i = 0; i < pScrn->numEntities; i++) {
|
|
if (pScrn->entityList[i] == entityIndex) {
|
|
peacc = xf86Entities[pScrn->entityList[i]]->access;
|
|
(*ptr) = peacc->next;
|
|
/* disable entity: call disable func */
|
|
if (peacc->pAccess)
|
|
peacc->pAccess->AccessDisable(peacc->pAccess->arg);
|
|
/* also disable fallback - just in case */
|
|
if (peacc->fallback)
|
|
peacc->fallback->AccessDisable(peacc->fallback->arg);
|
|
for (i++; i < pScrn->numEntities; i++)
|
|
pScrn->entityList[i-1] = pScrn->entityList[i];
|
|
pScrn->numEntities--;
|
|
xf86Entities[entityIndex]->inUse = FALSE;
|
|
break;
|
|
}
|
|
ptr = &(xf86Entities[pScrn->entityList[i]]->access->next);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* xf86ClearEntitiesForScreen() - called when a screen is deleted
|
|
* to mark it's entities unused. Called by xf86DeleteScreen().
|
|
*/
|
|
void
|
|
xf86ClearEntityListForScreen(int scrnIndex)
|
|
{
|
|
ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
|
|
EntityAccessPtr peacc;
|
|
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 */
|
|
peacc = xf86Entities[entityIndex]->access;
|
|
if (peacc->pAccess)
|
|
peacc->pAccess->AccessDisable(peacc->pAccess->arg);
|
|
/* and the fallback function */
|
|
if (peacc->fallback)
|
|
peacc->fallback->AccessDisable(peacc->fallback->arg);
|
|
/* shared resources are only needed when entity is active: remove */
|
|
xf86DeallocateResourcesForEntity(entityIndex, ResShared);
|
|
}
|
|
xfree(pScrn->entityList);
|
|
xfree(pScrn->entityInstanceList);
|
|
if (pScrn->CurrentAccess->pIoAccess == (EntityAccessPtr)pScrn->access)
|
|
pScrn->CurrentAccess->pIoAccess = NULL;
|
|
if (pScrn->CurrentAccess->pMemAccess == (EntityAccessPtr)pScrn->access)
|
|
pScrn->CurrentAccess->pMemAccess = NULL;
|
|
pScrn->entityList = NULL;
|
|
pScrn->entityInstanceList = NULL;
|
|
}
|
|
|
|
void
|
|
xf86DeallocateResourcesForEntity(int entityIndex, unsigned long type)
|
|
{
|
|
resPtr *pprev_next = &Acc;
|
|
resPtr res = Acc;
|
|
|
|
while (res) {
|
|
if (res->entityIndex == entityIndex &&
|
|
(type & ResAccMask & res->res_type))
|
|
{
|
|
(*pprev_next) = res->next;
|
|
xfree(res);
|
|
} else
|
|
pprev_next = &(res->next);
|
|
res = (*pprev_next);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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 >= 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->resources = xf86Entities[entityIndex]->resources;
|
|
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];
|
|
}
|
|
|
|
/*
|
|
* general generic disable function.
|
|
*/
|
|
static void
|
|
disableAccess(void)
|
|
{
|
|
int i;
|
|
xf86AccessPtr pacc;
|
|
EntityAccessPtr peacc;
|
|
|
|
/* call disable funcs and reset current access pointer */
|
|
/* the entity specific access funcs are in an enabled */
|
|
/* state - driver must restore their state explicitely */
|
|
for (i = 0; i < xf86NumScreens; i++) {
|
|
peacc = xf86Screens[i]->CurrentAccess->pIoAccess;
|
|
while (peacc) {
|
|
if (peacc->pAccess)
|
|
peacc->pAccess->AccessDisable(peacc->pAccess->arg);
|
|
peacc = peacc->next;
|
|
}
|
|
xf86Screens[i]->CurrentAccess->pIoAccess = NULL;
|
|
peacc = xf86Screens[i]->CurrentAccess->pMemAccess;
|
|
while (peacc) {
|
|
if (peacc->pAccess)
|
|
peacc->pAccess->AccessDisable(peacc->pAccess->arg);
|
|
peacc = peacc->next;
|
|
}
|
|
xf86Screens[i]->CurrentAccess->pMemAccess = NULL;
|
|
}
|
|
/* then call the generic entity disable funcs */
|
|
for (i = 0; i < xf86NumEntities; i++) {
|
|
pacc = xf86Entities[i]->access->fallback;
|
|
pacc->AccessDisable(pacc->arg);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clearAccess(void)
|
|
{
|
|
int i;
|
|
|
|
/* call disable funcs and reset current access pointer */
|
|
/* the entity specific access funcs are in an enabled */
|
|
/* state - driver must restore their state explicitely */
|
|
for (i = 0; i < xf86NumScreens; i++) {
|
|
xf86Screens[i]->CurrentAccess->pIoAccess = NULL;
|
|
xf86Screens[i]->CurrentAccess->pMemAccess = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Generic interface to bus specific code - add other buses here
|
|
*/
|
|
|
|
/*
|
|
* xf86AccessInit() - set up everything needed for access control
|
|
* called only once on first server generation.
|
|
*/
|
|
void
|
|
xf86AccessInit(void)
|
|
{
|
|
initPciState();
|
|
initPciBusState();
|
|
DisablePciBusAccess();
|
|
DisablePciAccess();
|
|
|
|
xf86ResAccessEnter = TRUE;
|
|
}
|
|
|
|
/*
|
|
* xf86AccessEnter() -- gets called to save the text mode VGA IO
|
|
* resources when reentering the server after a VT switch.
|
|
*/
|
|
void
|
|
xf86AccessEnter(void)
|
|
{
|
|
if (xf86ResAccessEnter)
|
|
return;
|
|
|
|
/*
|
|
* on enter we simply disable routing of special resources
|
|
* to any bus and let the RAC code to "open" the right bridges.
|
|
*/
|
|
PciBusStateEnter();
|
|
DisablePciBusAccess();
|
|
PciStateEnter();
|
|
disableAccess();
|
|
EntityEnter();
|
|
notifyStateChange(NOTIFY_ENTER);
|
|
xf86EnterServerState(SETUP);
|
|
xf86ResAccessEnter = TRUE;
|
|
}
|
|
|
|
/*
|
|
* xf86AccessLeave() -- prepares access for and calls the
|
|
* entityLeave() functions.
|
|
* xf86AccessLeaveState() --- gets called to restore the
|
|
* access to the VGA IO resources when switching VT or on
|
|
* server exit.
|
|
* This was split to call xf86AccessLeaveState() from
|
|
* ddxGiveUp().
|
|
*/
|
|
void
|
|
xf86AccessLeave(void)
|
|
{
|
|
if (!xf86ResAccessEnter)
|
|
return;
|
|
notifyStateChange(NOTIFY_LEAVE);
|
|
disableAccess();
|
|
DisablePciBusAccess();
|
|
EntityLeave();
|
|
}
|
|
|
|
void
|
|
xf86AccessLeaveState(void)
|
|
{
|
|
if (!xf86ResAccessEnter)
|
|
return;
|
|
xf86ResAccessEnter = FALSE;
|
|
PciStateLeave();
|
|
PciBusStateLeave();
|
|
}
|
|
|
|
/*
|
|
* xf86AccessRestoreState() - Restore the access registers to the
|
|
* state before X was started. This is handy for framebuffers.
|
|
*/
|
|
static void
|
|
xf86AccessRestoreState(void)
|
|
{
|
|
if (!xf86ResAccessEnter)
|
|
return;
|
|
PciStateLeave();
|
|
PciBusStateLeave();
|
|
}
|
|
|
|
/*
|
|
* xf86EnableAccess() -- enable access to controlled resources.
|
|
* To reduce latency when switching access the ScrnInfoRec has
|
|
* a linked list of the EntityAccPtr of all screen entities.
|
|
*/
|
|
/*
|
|
* switching access needs to be done in te following oder:
|
|
* disable
|
|
* 1. disable old entity
|
|
* 2. reroute bus
|
|
* 3. enable new entity
|
|
* Otherwise resources needed for access control might be shadowed
|
|
* by other resources!
|
|
*/
|
|
|
|
void
|
|
xf86EnableAccess(ScrnInfoPtr pScrn)
|
|
{
|
|
register EntityAccessPtr peAcc = (EntityAccessPtr) pScrn->access;
|
|
register EntityAccessPtr pceAcc;
|
|
register xf86AccessPtr pAcc;
|
|
EntityAccessPtr tmp;
|
|
|
|
#ifdef DEBUG
|
|
ErrorF("Enable access %i\n",pScrn->scrnIndex);
|
|
#endif
|
|
|
|
/* Entity is not under access control or currently enabled */
|
|
if (!pScrn->access) {
|
|
if (pScrn->busAccess) {
|
|
((BusAccPtr)pScrn->busAccess)->set_f(pScrn->busAccess);
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch (pScrn->resourceType) {
|
|
case IO:
|
|
pceAcc = pScrn->CurrentAccess->pIoAccess;
|
|
if (peAcc == pceAcc) {
|
|
return;
|
|
}
|
|
if (pScrn->CurrentAccess->pMemAccess == pceAcc)
|
|
pScrn->CurrentAccess->pMemAccess = NULL;
|
|
while (pceAcc) {
|
|
pAcc = pceAcc->pAccess;
|
|
if (pAcc)
|
|
pAcc->AccessDisable(pAcc->arg);
|
|
pceAcc = pceAcc->next;
|
|
}
|
|
if (pScrn->busAccess)
|
|
((BusAccPtr)pScrn->busAccess)->set_f(pScrn->busAccess);
|
|
while (peAcc) {
|
|
pAcc = peAcc->pAccess;
|
|
if (pAcc)
|
|
pAcc->AccessEnable(pAcc->arg);
|
|
peAcc = peAcc->next;
|
|
}
|
|
pScrn->CurrentAccess->pIoAccess = (EntityAccessPtr) pScrn->access;
|
|
return;
|
|
|
|
case MEM_IO:
|
|
pceAcc = pScrn->CurrentAccess->pIoAccess;
|
|
if (peAcc != pceAcc) { /* current Io != pAccess */
|
|
tmp = pceAcc;
|
|
while (pceAcc) {
|
|
pAcc = pceAcc->pAccess;
|
|
if (pAcc)
|
|
pAcc->AccessDisable(pAcc->arg);
|
|
pceAcc = pceAcc->next;
|
|
}
|
|
pceAcc = pScrn->CurrentAccess->pMemAccess;
|
|
if (peAcc != pceAcc /* current Mem != pAccess */
|
|
&& tmp !=pceAcc) {
|
|
while (pceAcc) {
|
|
pAcc = pceAcc->pAccess;
|
|
if (pAcc)
|
|
pAcc->AccessDisable(pAcc->arg);
|
|
pceAcc = pceAcc->next;
|
|
}
|
|
}
|
|
} else { /* current Io == pAccess */
|
|
pceAcc = pScrn->CurrentAccess->pMemAccess;
|
|
if (pceAcc == peAcc) { /* current Mem == pAccess */
|
|
return;
|
|
}
|
|
while (pceAcc) { /* current Mem != pAccess */
|
|
pAcc = pceAcc->pAccess;
|
|
if (pAcc)
|
|
pAcc->AccessDisable(pAcc->arg);
|
|
pceAcc = pceAcc->next;
|
|
}
|
|
}
|
|
if (pScrn->busAccess)
|
|
((BusAccPtr)pScrn->busAccess)->set_f(pScrn->busAccess);
|
|
while (peAcc) {
|
|
pAcc = peAcc->pAccess;
|
|
if (pAcc)
|
|
pAcc->AccessEnable(pAcc->arg);
|
|
peAcc = peAcc->next;
|
|
}
|
|
pScrn->CurrentAccess->pMemAccess =
|
|
pScrn->CurrentAccess->pIoAccess = (EntityAccessPtr) pScrn->access;
|
|
return;
|
|
|
|
case MEM:
|
|
pceAcc = pScrn->CurrentAccess->pMemAccess;
|
|
if (peAcc == pceAcc) {
|
|
return;
|
|
}
|
|
if (pScrn->CurrentAccess->pIoAccess == pceAcc)
|
|
pScrn->CurrentAccess->pIoAccess = NULL;
|
|
while (pceAcc) {
|
|
pAcc = pceAcc->pAccess;
|
|
if (pAcc)
|
|
pAcc->AccessDisable(pAcc->arg);
|
|
pceAcc = pceAcc->next;
|
|
}
|
|
if (pScrn->busAccess)
|
|
((BusAccPtr)pScrn->busAccess)->set_f(pScrn->busAccess);
|
|
while (peAcc) {
|
|
pAcc = peAcc->pAccess;
|
|
if (pAcc)
|
|
pAcc->AccessEnable(pAcc->arg);
|
|
peAcc = peAcc->next;
|
|
}
|
|
pScrn->CurrentAccess->pMemAccess = (EntityAccessPtr) pScrn->access;
|
|
return;
|
|
|
|
case NONE:
|
|
if (pScrn->busAccess) {
|
|
((BusAccPtr)pScrn->busAccess)->set_f(pScrn->busAccess);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
xf86SetCurrentAccess(Bool Enable, ScrnInfoPtr pScrn)
|
|
{
|
|
EntityAccessPtr pceAcc2 = NULL;
|
|
register EntityAccessPtr pceAcc = NULL;
|
|
register xf86AccessPtr pAcc;
|
|
|
|
|
|
switch(pScrn->resourceType) {
|
|
case IO:
|
|
pceAcc = pScrn->CurrentAccess->pIoAccess;
|
|
break;
|
|
case MEM:
|
|
pceAcc = pScrn->CurrentAccess->pMemAccess;
|
|
break;
|
|
case MEM_IO:
|
|
pceAcc = pScrn->CurrentAccess->pMemAccess;
|
|
pceAcc2 = pScrn->CurrentAccess->pIoAccess;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
while (pceAcc) {
|
|
pAcc = pceAcc->pAccess;
|
|
if (pAcc) {
|
|
if (Enable) {
|
|
pAcc->AccessEnable(pAcc->arg);
|
|
} else {
|
|
pAcc->AccessDisable(pAcc->arg);
|
|
}
|
|
}
|
|
pceAcc = pceAcc->next;
|
|
if (!pceAcc) {
|
|
pceAcc = pceAcc2;
|
|
pceAcc2 = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
xf86SetAccessFuncs(EntityInfoPtr pEnt, xf86SetAccessFuncPtr funcs,
|
|
xf86SetAccessFuncPtr oldFuncs)
|
|
{
|
|
AccessFuncPtr rac;
|
|
|
|
if (!xf86Entities[pEnt->index]->rac)
|
|
xf86Entities[pEnt->index]->rac = xnfcalloc(1,sizeof(AccessFuncRec));
|
|
|
|
rac = xf86Entities[pEnt->index]->rac;
|
|
|
|
rac->mem_new = funcs->mem;
|
|
rac->io_new = funcs->io;
|
|
rac->io_mem_new = funcs->io_mem;
|
|
|
|
rac->old = oldFuncs;
|
|
}
|
|
|
|
/*
|
|
* Conflict checking
|
|
*/
|
|
|
|
static memType
|
|
getMask(memType val)
|
|
{
|
|
memType mask = 0;
|
|
memType tmp = 0;
|
|
|
|
mask=~mask;
|
|
tmp = ~((~tmp) >> 1);
|
|
|
|
while (!(val & tmp)) {
|
|
mask = mask >> 1;
|
|
val = val << 1;
|
|
}
|
|
return mask;
|
|
}
|
|
|
|
/*
|
|
* checkConflictBlock() -- check for conflicts of a block resource range.
|
|
* If conflict is found return end of conflicting range. Else return 0.
|
|
*/
|
|
static memType
|
|
checkConflictBlock(resRange *range, resPtr pRes)
|
|
{
|
|
memType val,tmp,prev;
|
|
int i;
|
|
|
|
switch (pRes->res_type & ResExtMask) {
|
|
case ResBlock:
|
|
if (range->rBegin < pRes->block_end &&
|
|
range->rEnd > pRes->block_begin) {
|
|
#ifdef DEBUG
|
|
ErrorF("b-b conflict w: %lx %lx\n",
|
|
pRes->block_begin,pRes->block_end);
|
|
#endif
|
|
return pRes->block_end < range->rEnd ?
|
|
pRes->block_end : range->rEnd;
|
|
}
|
|
return 0;
|
|
case ResSparse:
|
|
if (pRes->sparse_base > range->rEnd) return 0;
|
|
|
|
val = (~pRes->sparse_mask | pRes->sparse_base) & getMask(range->rEnd);
|
|
#ifdef DEBUG
|
|
ErrorF("base = 0x%lx, mask = 0x%lx, begin = 0x%lx, end = 0x%lx ,"
|
|
"val = 0x%lx\n",
|
|
pRes->sparse_base, pRes->sparse_mask, range->rBegin,
|
|
range->rEnd, val);
|
|
#endif
|
|
i = sizeof(memType) * 8;
|
|
tmp = prev = pRes->sparse_base;
|
|
|
|
while (i) {
|
|
tmp |= 1<< (--i) & val;
|
|
if (tmp > range->rEnd)
|
|
tmp = prev;
|
|
else
|
|
prev = tmp;
|
|
}
|
|
if (tmp >= range->rBegin) {
|
|
#ifdef DEBUG
|
|
ErrorF("conflict found at: 0x%lx\n",tmp);
|
|
ErrorF("b-d conflict w: %lx %lx\n",
|
|
pRes->sparse_base,pRes->sparse_mask);
|
|
#endif
|
|
return tmp;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* checkConflictSparse() -- check for conflicts of a sparse resource range.
|
|
* If conflict is found return base of conflicting region. Else return 0.
|
|
*/
|
|
#define mt_max ~(memType)0
|
|
#define length sizeof(memType) * 8
|
|
static memType
|
|
checkConflictSparse(resRange *range, resPtr pRes)
|
|
{
|
|
memType val, tmp, prev;
|
|
int i;
|
|
|
|
switch (pRes->res_type & ResExtMask) {
|
|
case ResSparse:
|
|
tmp = pRes->sparse_mask & range->rMask;
|
|
if ((tmp & pRes->sparse_base) == (tmp & range->rBase)) {
|
|
#ifdef DEBUG
|
|
ErrorF("s-b conflict w: %lx %lx\n",
|
|
pRes->sparse_base,pRes->sparse_mask);
|
|
#endif
|
|
return pRes->sparse_mask;
|
|
}
|
|
return 0;
|
|
|
|
case ResBlock:
|
|
if (pRes->block_end < range->rBase) return 0;
|
|
|
|
val = (~range->rMask | range->rBase) & getMask(pRes->block_end);
|
|
i = length;
|
|
tmp = prev = range->rBase;
|
|
|
|
while (i) {
|
|
#ifdef DEBUG
|
|
ErrorF("tmp = 0x%lx\n",tmp);
|
|
#endif
|
|
tmp |= 1<< (--i) & val;
|
|
if (tmp > pRes->block_end)
|
|
tmp = prev;
|
|
else
|
|
prev = tmp;
|
|
}
|
|
if (tmp < pRes->block_begin)
|
|
return 0;
|
|
else {
|
|
/*
|
|
* now we subdivide the block region in sparse regions
|
|
* with base values = 2^n and find the smallest mask.
|
|
* This might be done in a simpler way....
|
|
*/
|
|
memType mask, m_mask = 0, base = pRes->block_begin;
|
|
int i;
|
|
while (base < pRes->block_end) {
|
|
for (i = 1; i < length; i++)
|
|
if ( base != (base & (mt_max << i))) break;
|
|
mask = mt_max >> (length - i);
|
|
do mask >>= 1;
|
|
while ((mask + base + 1) > pRes->block_end);
|
|
/* m_mask and are _inverted_ sparse masks */
|
|
m_mask = mask > m_mask ? mask : m_mask;
|
|
base = base + mask + 1;
|
|
}
|
|
#ifdef DEBUG
|
|
ErrorF("conflict found at: 0x%lx\n",tmp);
|
|
ErrorF("b-b conflict w: %lx %lx\n",
|
|
pRes->block_begin,pRes->block_end);
|
|
#endif
|
|
return ~m_mask;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#undef mt_max
|
|
#undef length
|
|
|
|
/*
|
|
* needCheck() -- this function decides whether to check for conflicts
|
|
* depending on the types of the resource ranges and their locations
|
|
*/
|
|
static Bool
|
|
needCheck(resPtr pRes, unsigned long type, int entityIndex, xf86State state)
|
|
{
|
|
/* the same entity shouldn't conflict with itself */
|
|
ScrnInfoPtr pScrn;
|
|
int i;
|
|
BusType loc = BUS_NONE;
|
|
BusType r_loc = BUS_NONE;
|
|
|
|
if ((pRes->res_type & ResTypeMask) != (type & ResTypeMask))
|
|
return FALSE;
|
|
|
|
/*
|
|
* Resources set by BIOS (ResBios) are allowed to conflict
|
|
* with resources marked (ResBios).
|
|
*/
|
|
if (pRes->res_type & type & ResBios)
|
|
return FALSE;
|
|
|
|
if (type & pRes->res_type & ResUnused)
|
|
return FALSE;
|
|
|
|
if (state == OPERATING) {
|
|
if (type & ResDisableOpr || pRes->res_type & ResDisableOpr)
|
|
return FALSE;
|
|
if (type & pRes->res_type & ResUnusedOpr) return FALSE;
|
|
/*
|
|
* Maybe we should have ResUnused set The resUnusedOpr
|
|
* bit, too. This way we could avoid this confusion
|
|
*/
|
|
if ((type & ResUnusedOpr && pRes->res_type & ResUnused) ||
|
|
(type & ResUnused && pRes->res_type & ResUnusedOpr))
|
|
return FALSE;
|
|
}
|
|
|
|
if (entityIndex > -1)
|
|
loc = xf86Entities[entityIndex]->busType;
|
|
if (pRes->entityIndex > -1)
|
|
r_loc = xf86Entities[pRes->entityIndex]->busType;
|
|
|
|
if ((type & ResAccMask) == ResShared &&
|
|
(pRes->res_type & ResAccMask) == ResShared)
|
|
return FALSE;
|
|
|
|
if (pRes->entityIndex == entityIndex) return FALSE;
|
|
|
|
if (pRes->entityIndex > -1 &&
|
|
(pScrn = xf86FindScreenForEntity(entityIndex))) {
|
|
for (i = 0; i < pScrn->numEntities; i++)
|
|
if (pScrn->entityList[i] == pRes->entityIndex) return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* checkConflict() - main conflict checking function which all other
|
|
* function call.
|
|
*/
|
|
static memType
|
|
checkConflict(resRange *rgp, resPtr pRes, int entityIndex,
|
|
xf86State state, Bool ignoreIdentical)
|
|
{
|
|
memType ret;
|
|
|
|
while(pRes) {
|
|
if (!needCheck(pRes,rgp->type, entityIndex ,state)) {
|
|
pRes = pRes->next;
|
|
continue;
|
|
}
|
|
switch (rgp->type & ResExtMask) {
|
|
case ResBlock:
|
|
if (rgp->rEnd < rgp->rBegin) {
|
|
xf86Msg(X_ERROR,"end of block range 0x%lx < begin 0x%lx\n",
|
|
(long)rgp->rEnd, (long)rgp->rBegin);
|
|
return 0;
|
|
}
|
|
if ((ret = checkConflictBlock(rgp, pRes))) {
|
|
if (!ignoreIdentical || (rgp->rBegin != pRes->block_begin)
|
|
|| (rgp->rEnd != pRes->block_end))
|
|
return ret;
|
|
}
|
|
break;
|
|
case ResSparse:
|
|
if ((rgp->rBase & rgp->rMask) != rgp->rBase) {
|
|
xf86Msg(X_ERROR,"sparse io range (base: 0x%lx mask: 0x%lx)"
|
|
"doesn't satisfy (base & mask = mask)\n",
|
|
(long)rgp->rBase, (long)rgp->rMask);
|
|
return 0;
|
|
}
|
|
if ((ret = checkConflictSparse(rgp, pRes))) {
|
|
if (!ignoreIdentical || (rgp->rBase != pRes->sparse_base)
|
|
|| (rgp->rMask != pRes->sparse_mask))
|
|
return ret;
|
|
}
|
|
break;
|
|
}
|
|
pRes = pRes->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* xf86ChkConflict() - This function is the low level interface to
|
|
* the resource broker that gets exported. Tests all resources ie.
|
|
* performs test with SETUP flag.
|
|
*/
|
|
static memType
|
|
xf86ChkConflict(resRange *rgp, int entityIndex)
|
|
{
|
|
return checkConflict(rgp, Acc, entityIndex, SETUP, FALSE);
|
|
}
|
|
|
|
/*
|
|
* Resources List handling
|
|
*/
|
|
|
|
static resPtr
|
|
xf86JoinResLists(resPtr rlist1, resPtr rlist2)
|
|
{
|
|
resPtr pRes;
|
|
|
|
if (!rlist1)
|
|
return rlist2;
|
|
|
|
if (!rlist2)
|
|
return rlist1;
|
|
|
|
for (pRes = rlist1; pRes->next; pRes = pRes->next)
|
|
;
|
|
pRes->next = rlist2;
|
|
return rlist1;
|
|
}
|
|
|
|
resPtr
|
|
xf86AddResToList(resPtr rlist, resRange *range, int entityIndex)
|
|
{
|
|
resPtr new;
|
|
|
|
switch (range->type & ResExtMask) {
|
|
case ResBlock:
|
|
if (range->rEnd < range->rBegin) {
|
|
xf86Msg(X_ERROR,"end of block range 0x%lx < begin 0x%lx\n",
|
|
(long)range->rEnd, (long)range->rBegin);
|
|
return rlist;
|
|
}
|
|
break;
|
|
case ResSparse:
|
|
if ((range->rBase & range->rMask) != range->rBase) {
|
|
xf86Msg(X_ERROR,"sparse io range (base: 0x%lx mask: 0x%lx)"
|
|
"doesn't satisfy (base & mask = mask)\n",
|
|
(long)range->rBase, (long)range->rMask);
|
|
return rlist;
|
|
}
|
|
break;
|
|
}
|
|
|
|
new = xnfalloc(sizeof(resRec));
|
|
/*
|
|
* Only background resources may be registered with ResBios
|
|
* and ResEstimated set. Other resources only set it for
|
|
* testing.
|
|
*/
|
|
if (entityIndex != (-1))
|
|
range->type &= ~(ResBios | ResEstimated);
|
|
new->val = *range;
|
|
new->entityIndex = entityIndex;
|
|
new->next = rlist;
|
|
return new;
|
|
}
|
|
|
|
void
|
|
xf86FreeResList(resPtr rlist)
|
|
{
|
|
resPtr pRes;
|
|
|
|
if (!rlist)
|
|
return;
|
|
|
|
for (pRes = rlist->next; pRes; rlist = pRes, pRes = pRes->next)
|
|
xfree(rlist);
|
|
xfree(rlist);
|
|
}
|
|
|
|
static resPtr
|
|
xf86DupResList(const resPtr rlist)
|
|
{
|
|
resPtr pRes, ret, prev, new;
|
|
|
|
if (!rlist)
|
|
return NULL;
|
|
|
|
ret = xnfalloc(sizeof(resRec));
|
|
*ret = *rlist;
|
|
prev = ret;
|
|
for (pRes = rlist->next; pRes; pRes = pRes->next) {
|
|
new = xnfalloc(sizeof(resRec));
|
|
*new = *pRes;
|
|
prev->next = new;
|
|
prev = new;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
xf86PrintResList(int verb, resPtr list)
|
|
{
|
|
int i = 0;
|
|
const char *s, *r;
|
|
resPtr tmp = list;
|
|
unsigned long type;
|
|
|
|
if (!list)
|
|
return;
|
|
|
|
type = ResMem;
|
|
r = "M";
|
|
while (1) {
|
|
while (list) {
|
|
if ((list->res_type & ResPhysMask) == type) {
|
|
switch (list->res_type & ResExtMask) {
|
|
case ResBlock:
|
|
xf86ErrorFVerb(verb,
|
|
"\t[%d] %d\t%ld\t0x%08lx - 0x%08lx (0x%lx)",
|
|
i, list->entityIndex,
|
|
(list->res_type & ResDomain) >> 24,
|
|
(long)list->block_begin,
|
|
(long)list->block_end,
|
|
(long)(list->block_end -
|
|
list->block_begin + 1));
|
|
break;
|
|
case ResSparse:
|
|
xf86ErrorFVerb(verb, "\t[%d] %d\t%ld\t0x%08lx - 0x%08lx ",
|
|
i, list->entityIndex,
|
|
(list->res_type & ResDomain) >> 24,
|
|
(long)list->sparse_base,
|
|
(long)list->sparse_mask);
|
|
break;
|
|
default:
|
|
list = list->next;
|
|
continue;
|
|
}
|
|
xf86ErrorFVerb(verb, " %s", r);
|
|
switch (list->res_type & ResAccMask) {
|
|
case ResExclusive:
|
|
if (list->res_type & ResUnused)
|
|
s = "x";
|
|
else
|
|
s = "X";
|
|
break;
|
|
case ResShared:
|
|
if (list->res_type & ResUnused)
|
|
s = "s";
|
|
else
|
|
s = "S";
|
|
break;
|
|
default:
|
|
s = "?";
|
|
}
|
|
xf86ErrorFVerb(verb, "%s", s);
|
|
switch (list->res_type & ResExtMask) {
|
|
case ResBlock:
|
|
s = "[B]";
|
|
break;
|
|
case ResSparse:
|
|
s = "[S]";
|
|
break;
|
|
default:
|
|
s = "[?]";
|
|
}
|
|
xf86ErrorFVerb(verb, "%s", s);
|
|
if (list->res_type & ResInit)
|
|
xf86ErrorFVerb(verb, "t");
|
|
if (list->res_type & ResBios)
|
|
xf86ErrorFVerb(verb, "(B)");
|
|
if (list->res_type & ResBus)
|
|
xf86ErrorFVerb(verb, "(b)");
|
|
if (list->res_type & ResOprMask) {
|
|
switch (list->res_type & ResOprMask) {
|
|
case ResUnusedOpr:
|
|
s = "(OprU)";
|
|
break;
|
|
case ResDisableOpr:
|
|
s = "(OprD)";
|
|
break;
|
|
default:
|
|
s = "(Opr?)";
|
|
break;
|
|
}
|
|
xf86ErrorFVerb(verb, "%s", s);
|
|
}
|
|
xf86ErrorFVerb(verb, "\n");
|
|
i++;
|
|
}
|
|
list = list->next;
|
|
}
|
|
if (type == ResIo) break;
|
|
type = ResIo;
|
|
r = "I";
|
|
list = tmp;
|
|
}
|
|
}
|
|
|
|
resPtr
|
|
xf86AddRangesToList(resPtr list, resRange *pRange, int entityIndex)
|
|
{
|
|
while(pRange && pRange->type != ResEnd) {
|
|
list = xf86AddResToList(list,pRange,entityIndex);
|
|
pRange++;
|
|
}
|
|
return list;
|
|
}
|
|
|
|
void
|
|
xf86ResourceBrokerInit(void)
|
|
{
|
|
Acc = NULL;
|
|
|
|
/* Get the ranges used exclusively by the system */
|
|
Acc = xf86AccResFromOS(Acc);
|
|
xf86MsgVerb(X_INFO, 3, "System resource ranges:\n");
|
|
xf86PrintResList(3, Acc);
|
|
}
|
|
|
|
/*
|
|
* Resource registration
|
|
*/
|
|
|
|
static void
|
|
convertRange2Host(int entityIndex, resRange *pRange)
|
|
{
|
|
if (pRange->type & ResBus) {
|
|
switch (xf86Entities[entityIndex]->busType) {
|
|
case BUS_PCI:
|
|
pciConvertRange2Host(entityIndex,pRange);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
pRange->type &= ~ResBus;
|
|
}
|
|
}
|
|
|
|
static void
|
|
xf86ConvertListToHost(int entityIndex, resPtr list)
|
|
{
|
|
while (list) {
|
|
convertRange2Host(entityIndex, &list->val);
|
|
list = list->next;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* xf86RegisterResources() -- attempts to register listed resources.
|
|
* Returns a resPtr listing all resources not successfully registered, by
|
|
* which we mean, NULL.
|
|
*/
|
|
|
|
resPtr
|
|
xf86RegisterResources(int entityIndex, resList list, unsigned long access)
|
|
{
|
|
resRange range;
|
|
resList list_f = NULL;
|
|
|
|
if (!list)
|
|
return NULL;
|
|
|
|
while(list->type != ResEnd) {
|
|
range = *list;
|
|
|
|
convertRange2Host(entityIndex,&range);
|
|
|
|
if ((access != ResNone) && (access & ResAccMask)) {
|
|
range.type = (range.type & ~ResAccMask) | (access & ResAccMask);
|
|
}
|
|
range.type &= ~ResEstimated; /* Not allowed for drivers */
|
|
Acc = xf86AddResToList(Acc,&range,entityIndex);
|
|
list++;
|
|
}
|
|
if (list_f)
|
|
xfree(list_f);
|
|
|
|
#ifdef DEBUG
|
|
xf86MsgVerb(X_INFO, 3,"Resources after driver initialization\n");
|
|
xf86PrintResList(3, Acc);
|
|
#endif
|
|
return NULL;
|
|
|
|
}
|
|
|
|
static void
|
|
busTypeSpecific(EntityPtr pEnt, xf86AccessPtr *acc_mem,
|
|
xf86AccessPtr *acc_io, xf86AccessPtr *acc_mem_io)
|
|
{
|
|
switch (pEnt->bus.type) {
|
|
case BUS_SBUS:
|
|
*acc_mem = *acc_io = *acc_mem_io = &AccessNULL;
|
|
break;
|
|
case BUS_PCI: {
|
|
struct pci_device *const dev = pEnt->bus.id.pci;
|
|
|
|
if ((dev != NULL) && ((void *)dev->user_data != NULL)) {
|
|
pciAccPtr const paccp = (pciAccPtr) dev->user_data;
|
|
|
|
*acc_io = & paccp->ioAccess;
|
|
*acc_mem = & paccp->memAccess;
|
|
*acc_mem_io = & paccp->io_memAccess;
|
|
}
|
|
else {
|
|
/* FIXME: This is an error path. We should probably have an
|
|
* FIXME: assertion here or something.
|
|
*/
|
|
*acc_io = NULL;
|
|
*acc_mem = NULL;
|
|
*acc_mem_io = NULL;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
*acc_mem = *acc_io = *acc_mem_io = NULL;
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
setAccess(EntityPtr pEnt, xf86State state)
|
|
{
|
|
|
|
xf86AccessPtr acc_mem, acc_io, acc_mem_io;
|
|
xf86AccessPtr org_mem = NULL, org_io = NULL, org_mem_io = NULL;
|
|
int prop;
|
|
|
|
busTypeSpecific(pEnt, &acc_mem, &acc_io, &acc_mem_io);
|
|
|
|
/* The replacement function needs to handle _all_ shared resources */
|
|
/* unless they are handeled locally and disabled otherwise */
|
|
if (pEnt->rac) {
|
|
if (pEnt->rac->io_new) {
|
|
org_io = acc_io;
|
|
acc_io = pEnt->rac->io_new;
|
|
}
|
|
if (pEnt->rac->mem_new) {
|
|
org_mem = acc_mem;
|
|
acc_mem = pEnt->rac->mem_new;
|
|
}
|
|
if (pEnt->rac->io_mem_new) {
|
|
org_mem_io = acc_mem_io;
|
|
acc_mem_io = pEnt->rac->io_mem_new;
|
|
}
|
|
}
|
|
|
|
if (state == OPERATING) {
|
|
prop = pEnt->entityProp;
|
|
switch(pEnt->entityProp & NEED_SHARED) {
|
|
case NEED_SHARED:
|
|
pEnt->access->rt = MEM_IO;
|
|
break;
|
|
case NEED_IO_SHARED:
|
|
pEnt->access->rt = IO;
|
|
break;
|
|
case NEED_MEM_SHARED:
|
|
pEnt->access->rt = MEM;
|
|
break;
|
|
default:
|
|
pEnt->access->rt = NONE;
|
|
}
|
|
} else {
|
|
prop = NEED_SHARED | NEED_MEM | NEED_IO;
|
|
pEnt->access->rt = MEM_IO;
|
|
}
|
|
|
|
switch(pEnt->access->rt) {
|
|
case IO:
|
|
pEnt->access->pAccess = acc_io;
|
|
break;
|
|
case MEM:
|
|
pEnt->access->pAccess = acc_mem;
|
|
break;
|
|
case MEM_IO:
|
|
pEnt->access->pAccess = acc_mem_io;
|
|
break;
|
|
default: /* no conflicts at all */
|
|
pEnt->access->pAccess = NULL; /* remove from RAC */
|
|
break;
|
|
}
|
|
|
|
if (org_io) {
|
|
/* does the driver want the old access func? */
|
|
if (pEnt->rac->old) {
|
|
/* give it to the driver, leave state disabled */
|
|
pEnt->rac->old->io = org_io;
|
|
} else {
|
|
/* driver doesn't want it - enable generic access */
|
|
org_io->AccessEnable(org_io->arg);
|
|
}
|
|
}
|
|
|
|
if (org_mem_io) {
|
|
/* does the driver want the old access func? */
|
|
if (pEnt->rac->old) {
|
|
/* give it to the driver, leave state disabled */
|
|
pEnt->rac->old->io_mem = org_mem_io;
|
|
} else {
|
|
/* driver doesn't want it - enable generic access */
|
|
org_mem_io->AccessEnable(org_mem_io->arg);
|
|
}
|
|
}
|
|
|
|
if (org_mem) {
|
|
/* does the driver want the old access func? */
|
|
if (pEnt->rac->old) {
|
|
/* give it to the driver, leave state disabled */
|
|
pEnt->rac->old->mem = org_mem;
|
|
} else {
|
|
/* driver doesn't want it - enable generic access */
|
|
org_mem->AccessEnable(org_mem->arg);
|
|
}
|
|
}
|
|
|
|
if (!(prop & NEED_MEM_SHARED)){
|
|
if (prop & NEED_MEM) {
|
|
if (acc_mem)
|
|
acc_mem->AccessEnable(acc_mem->arg);
|
|
} else {
|
|
if (acc_mem)
|
|
acc_mem->AccessDisable(acc_mem->arg);
|
|
}
|
|
}
|
|
|
|
if (!(prop & NEED_IO_SHARED)) {
|
|
if (prop & NEED_IO) {
|
|
if (acc_io)
|
|
acc_io->AccessEnable(acc_io->arg);
|
|
} else {
|
|
if (acc_io)
|
|
acc_io->AccessDisable(acc_io->arg);
|
|
}
|
|
}
|
|
|
|
/* disable shared resources */
|
|
if (pEnt->access->pAccess)
|
|
pEnt->access->pAccess->AccessDisable(pEnt->access->pAccess->arg);
|
|
|
|
/*
|
|
* If device is not under access control it is enabled.
|
|
* If it needs bus routing do it here as it isn't bus
|
|
* type specific. Any conflicts should be checked at this
|
|
* stage
|
|
*/
|
|
if (!pEnt->access->pAccess
|
|
&& (pEnt->entityProp & (state == SETUP ? NEED_VGA_ROUTED_SETUP :
|
|
NEED_VGA_ROUTED)))
|
|
((BusAccPtr)pEnt->busAcc)->set_f(pEnt->busAcc);
|
|
}
|
|
|
|
|
|
/*
|
|
* xf86EnterServerState() -- set state the server is in.
|
|
*/
|
|
|
|
typedef enum { TRI_UNSET, TRI_TRUE, TRI_FALSE } TriState;
|
|
|
|
static void
|
|
SetSIGIOForState(xf86State state)
|
|
{
|
|
static int sigio_state;
|
|
static TriState sigio_blocked = TRI_UNSET;
|
|
|
|
if ((state == SETUP) && (sigio_blocked != TRI_TRUE)) {
|
|
sigio_state = xf86BlockSIGIO();
|
|
sigio_blocked = TRI_TRUE;
|
|
} else if ((state == OPERATING) && (sigio_blocked != TRI_UNSET)) {
|
|
xf86UnblockSIGIO(sigio_state);
|
|
sigio_blocked = TRI_FALSE;
|
|
}
|
|
}
|
|
|
|
void
|
|
xf86EnterServerState(xf86State state)
|
|
{
|
|
EntityPtr pEnt;
|
|
ScrnInfoPtr pScrn;
|
|
int i,j;
|
|
int needVGA = 0;
|
|
resType rt;
|
|
/*
|
|
* This is a good place to block SIGIO during SETUP state.
|
|
* SIGIO should be blocked in SETUP state otherwise (u)sleep()
|
|
* might get interrupted early.
|
|
* We take care not to call xf86BlockSIGIO() twice.
|
|
*/
|
|
SetSIGIOForState(state);
|
|
#ifdef DEBUG
|
|
if (state == SETUP)
|
|
ErrorF("Entering SETUP state\n");
|
|
else
|
|
ErrorF("Entering OPERATING state\n");
|
|
#endif
|
|
|
|
/* When servicing a dumb framebuffer we don't need to do anything */
|
|
if (doFramebufferMode) return;
|
|
|
|
for (i=0; i<xf86NumScreens; i++) {
|
|
pScrn = xf86Screens[i];
|
|
j = pScrn->entityList[pScrn->numEntities - 1];
|
|
pScrn->access = xf86Entities[j]->access;
|
|
|
|
for (j = 0; j<xf86Screens[i]->numEntities; j++) {
|
|
pEnt = xf86Entities[xf86Screens[i]->entityList[j]];
|
|
if (pEnt->entityProp & (state == SETUP ? NEED_VGA_ROUTED_SETUP
|
|
: NEED_VGA_ROUTED))
|
|
xf86Screens[i]->busAccess = pEnt->busAcc;
|
|
}
|
|
if (xf86Screens[i]->busAccess)
|
|
needVGA ++;
|
|
}
|
|
|
|
/*
|
|
* if we just have one screen we don't have RAC.
|
|
* Therefore just enable the screen and return.
|
|
*/
|
|
if (!needRAC) {
|
|
xf86EnableAccess(xf86Screens[0]);
|
|
notifyStateChange(NOTIFY_ENABLE);
|
|
return;
|
|
}
|
|
|
|
if (state == SETUP)
|
|
notifyStateChange(NOTIFY_SETUP_TRANSITION);
|
|
else
|
|
notifyStateChange(NOTIFY_OPERATING_TRANSITION);
|
|
|
|
clearAccess();
|
|
for (i=0; i<xf86NumScreens;i++) {
|
|
|
|
rt = NONE;
|
|
|
|
for (j = 0; j<xf86Screens[i]->numEntities; j++) {
|
|
pEnt = xf86Entities[xf86Screens[i]->entityList[j]];
|
|
setAccess(pEnt,state);
|
|
|
|
if (pEnt->access->rt != NONE) {
|
|
if (rt != NONE && rt != pEnt->access->rt)
|
|
rt = MEM_IO;
|
|
else
|
|
rt = pEnt->access->rt;
|
|
}
|
|
}
|
|
xf86Screens[i]->resourceType = rt;
|
|
if (rt == NONE) {
|
|
xf86Screens[i]->access = NULL;
|
|
if (needVGA < 2)
|
|
xf86Screens[i]->busAccess = NULL;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (xf86Screens[i]->busAccess)
|
|
ErrorF("Screen %i setting vga route\n",i);
|
|
#endif
|
|
switch (rt) {
|
|
case MEM_IO:
|
|
xf86MsgVerb(X_INFO, 3, "Screen %i shares mem & io resources\n",i);
|
|
break;
|
|
case IO:
|
|
xf86MsgVerb(X_INFO, 3, "Screen %i shares io resources\n",i);
|
|
break;
|
|
case MEM:
|
|
xf86MsgVerb(X_INFO, 3, "Screen %i shares mem resources\n",i);
|
|
break;
|
|
default:
|
|
xf86MsgVerb(X_INFO, 3, "Entity %i shares no resources\n",i);
|
|
break;
|
|
}
|
|
}
|
|
if (state == SETUP)
|
|
notifyStateChange(NOTIFY_SETUP);
|
|
else
|
|
notifyStateChange(NOTIFY_OPERATING);
|
|
}
|
|
|
|
/*
|
|
* xf86SetOperatingState() -- Set ResOperMask for resources listed.
|
|
*/
|
|
resPtr
|
|
xf86SetOperatingState(resList list, int entityIndex, int mask)
|
|
{
|
|
resPtr acc;
|
|
resPtr r_fail = NULL;
|
|
resRange range;
|
|
|
|
while (list->type != ResEnd) {
|
|
range = *list;
|
|
convertRange2Host(entityIndex,&range);
|
|
|
|
acc = Acc;
|
|
while (acc) {
|
|
#define MASK (ResTypeMask | ResExtMask)
|
|
if ((acc->entityIndex == entityIndex)
|
|
&& (acc->val.a == range.a) && (acc->val.b == range.b)
|
|
&& ((acc->val.type & MASK) == (range.type & MASK)))
|
|
break;
|
|
#undef MASK
|
|
acc = acc->next;
|
|
}
|
|
if (acc)
|
|
acc->val.type = (acc->val.type & ~ResOprMask)
|
|
| (mask & ResOprMask);
|
|
else {
|
|
r_fail = xf86AddResToList(r_fail,&range,entityIndex);
|
|
}
|
|
list ++;
|
|
}
|
|
|
|
return r_fail;
|
|
}
|
|
|
|
/*
|
|
* Stage specific code
|
|
*/
|
|
|
|
/*
|
|
* xf86ClaimFixedResources() is used to allocate non-relocatable resources.
|
|
* This should only be done by a driver's Probe() function.
|
|
*/
|
|
void
|
|
xf86ClaimFixedResources(resList list, int entityIndex)
|
|
{
|
|
resPtr ptr = NULL;
|
|
resRange range;
|
|
|
|
if (!list) return;
|
|
|
|
while (list->type !=ResEnd) {
|
|
range = *list;
|
|
|
|
convertRange2Host(entityIndex,&range);
|
|
|
|
range.type &= ~ResEstimated; /* Not allowed for drivers */
|
|
switch (range.type & ResAccMask) {
|
|
case ResExclusive:
|
|
if (!xf86ChkConflict(&range, entityIndex)) {
|
|
Acc = xf86AddResToList(Acc, &range, entityIndex);
|
|
} else FatalError("xf86ClaimFixedResources conflict\n");
|
|
break;
|
|
case ResShared:
|
|
/* at this stage the resources are just added to the
|
|
* EntityRec. After the Probe() phase this list is checked by
|
|
* xf86PostProbe(). All resources which don't
|
|
* conflict with already allocated ones are allocated
|
|
* and removed from the EntityRec. Thus a non-empty resource
|
|
* list in the EntityRec indicates resource conflicts the
|
|
* driver should either handle or fail.
|
|
*/
|
|
if (xf86Entities[entityIndex]->active)
|
|
ptr = xf86AddResToList(ptr,&range,entityIndex);
|
|
break;
|
|
}
|
|
list++;
|
|
}
|
|
xf86Entities[entityIndex]->resources =
|
|
xf86JoinResLists(xf86Entities[entityIndex]->resources,ptr);
|
|
xf86MsgVerb(X_INFO, 3,
|
|
"resource ranges after xf86ClaimFixedResources() call:\n");
|
|
xf86PrintResList(3,Acc);
|
|
#ifdef DEBUG
|
|
if (ptr) {
|
|
xf86MsgVerb(X_INFO, 3, "to be registered later:\n");
|
|
xf86PrintResList(3,ptr);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
checkRoutingForScreens(xf86State state)
|
|
{
|
|
resList list = resVgaUnusedExclusive;
|
|
resPtr pResVGA = NULL;
|
|
resPtr pResVGAHost;
|
|
pointer vga = NULL;
|
|
int i,j;
|
|
int entityIndex;
|
|
EntityPtr pEnt;
|
|
resPtr pAcc;
|
|
resRange range;
|
|
|
|
/*
|
|
* find devices that need VGA routed: ie the ones that have
|
|
* registered VGA resources without ResUnused. ResUnused
|
|
* doesn't conflict with itself therefore use it here.
|
|
*/
|
|
while (list->type != ResEnd) { /* create resPtr from resList for VGA */
|
|
range = *list;
|
|
range.type &= ~(ResBios | ResEstimated); /* if set remove them */
|
|
pResVGA = xf86AddResToList(pResVGA, &range, -1);
|
|
list++;
|
|
}
|
|
|
|
for (i = 0; i < xf86NumScreens; i++) {
|
|
for (j = 0; j < xf86Screens[i]->numEntities; j++) {
|
|
entityIndex = xf86Screens[i]->entityList[j];
|
|
pEnt = xf86Entities[entityIndex];
|
|
pAcc = Acc;
|
|
vga = NULL;
|
|
pResVGAHost = xf86DupResList(pResVGA);
|
|
xf86ConvertListToHost(entityIndex,pResVGAHost);
|
|
while (pAcc) {
|
|
if (pAcc->entityIndex == entityIndex)
|
|
if (checkConflict(&pAcc->val, pResVGAHost,
|
|
entityIndex, state, FALSE)) {
|
|
if (vga && vga != pEnt->busAcc) {
|
|
xf86Msg(X_ERROR, "Screen %i needs vga routed to"
|
|
"different buses - deleting\n",i);
|
|
xf86DeleteScreen(i--,0);
|
|
}
|
|
#ifdef DEBUG
|
|
{
|
|
resPtr rlist = xf86AddResToList(NULL,&pAcc->val,
|
|
pAcc->entityIndex);
|
|
xf86MsgVerb(X_INFO,3,"====== %s\n",
|
|
state == OPERATING ? "OPERATING"
|
|
: "SETUP");
|
|
xf86MsgVerb(X_INFO,3,"%s Resource:\n",
|
|
(pAcc->val.type) & ResMem ? "Mem" :"Io");
|
|
xf86PrintResList(3,rlist);
|
|
xf86FreeResList(rlist);
|
|
xf86MsgVerb(X_INFO,3,"Conflicts with:\n");
|
|
xf86PrintResList(3,pResVGAHost);
|
|
xf86MsgVerb(X_INFO,3,"=====\n");
|
|
}
|
|
#endif
|
|
vga = pEnt->busAcc;
|
|
pEnt->entityProp |= (state == SETUP
|
|
? NEED_VGA_ROUTED_SETUP : NEED_VGA_ROUTED);
|
|
if (state == OPERATING) {
|
|
if (pAcc->val.type & ResMem)
|
|
pEnt->entityProp |= NEED_VGA_MEM;
|
|
else
|
|
pEnt->entityProp |= NEED_VGA_IO;
|
|
}
|
|
}
|
|
pAcc = pAcc->next;
|
|
}
|
|
if (vga)
|
|
xf86MsgVerb(X_INFO, 3,"Setting vga for screen %i.\n",i);
|
|
xf86FreeResList(pResVGAHost);
|
|
}
|
|
}
|
|
xf86FreeResList(pResVGA);
|
|
}
|
|
|
|
/*
|
|
* xf86PostProbe() -- Allocate all non conflicting resources
|
|
* This function gets called by xf86Init().
|
|
*/
|
|
void
|
|
xf86PostProbe(void)
|
|
{
|
|
memType val;
|
|
int i,j;
|
|
resPtr resp, acc, tmp, resp_x;
|
|
|
|
if (fbSlotClaimed) {
|
|
if (pciSlotClaimed
|
|
#if (defined(__sparc__) || defined(__sparc)) && !defined(__OpenBSD__)
|
|
|| sbusSlotClaimed
|
|
#endif
|
|
) {
|
|
FatalError("Cannot run in framebuffer mode. Please specify busIDs "
|
|
" for all framebuffer devices\n");
|
|
return;
|
|
} else {
|
|
xf86Msg(X_INFO,"Running in FRAMEBUFFER Mode\n");
|
|
xf86AccessRestoreState();
|
|
notifyStateChange(NOTIFY_ENABLE);
|
|
doFramebufferMode = TRUE;
|
|
|
|
return;
|
|
}
|
|
}
|
|
acc = tmp = xf86DupResList(Acc);
|
|
|
|
for (i=0; i<xf86NumEntities; i++) {
|
|
resp = xf86Entities[i]->resources;
|
|
xf86Entities[i]->resources = NULL;
|
|
resp_x = NULL;
|
|
while (resp) {
|
|
if (! (val = checkConflict(&resp->val,acc,i,SETUP,FALSE))) {
|
|
resp->res_type &= ~(ResBios); /* just used for chkConflict() */
|
|
tmp = resp_x;
|
|
resp_x = resp;
|
|
resp = resp->next;
|
|
resp_x->next = tmp;
|
|
} else {
|
|
xf86MsgVerb(X_INFO, 3, "Found conflict at: 0x%lx\n",
|
|
(long)val);
|
|
resp->res_type &= ~ResEstimated;
|
|
tmp = xf86Entities[i]->resources;
|
|
xf86Entities[i]->resources = resp;
|
|
resp = resp->next;
|
|
xf86Entities[i]->resources->next = tmp;
|
|
}
|
|
}
|
|
xf86JoinResLists(Acc,resp_x);
|
|
}
|
|
xf86FreeResList(acc);
|
|
|
|
xf86MsgVerb(X_INFO, 3, "resource ranges after probing:\n");
|
|
xf86PrintResList(3, Acc);
|
|
checkRoutingForScreens(SETUP);
|
|
|
|
for (i = 0; i < xf86NumScreens; i++) {
|
|
for (j = 0; j<xf86Screens[i]->numEntities; j++) {
|
|
EntityPtr pEnt = xf86Entities[xf86Screens[i]->entityList[j]];
|
|
if ((pEnt->entityProp & NEED_VGA_ROUTED_SETUP) &&
|
|
((xf86Screens[i]->busAccess = pEnt->busAcc)))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
checkRequiredResources(int entityIndex)
|
|
{
|
|
resRange range;
|
|
resPtr pAcc = Acc;
|
|
const EntityPtr pEnt = xf86Entities[entityIndex];
|
|
while (pAcc) {
|
|
if (pAcc->entityIndex == entityIndex) {
|
|
range = pAcc->val;
|
|
/* ResAny to find conflicts with anything. */
|
|
range.type = (range.type & ~ResAccMask) | ResAny | ResBios;
|
|
if (checkConflict(&range,Acc,entityIndex,OPERATING,FALSE))
|
|
switch (pAcc->res_type & ResPhysMask) {
|
|
case ResMem:
|
|
pEnt->entityProp |= NEED_MEM_SHARED;
|
|
break;
|
|
case ResIo:
|
|
pEnt->entityProp |= NEED_IO_SHARED;
|
|
break;
|
|
}
|
|
if (!(pAcc->res_type & ResOprMask)) {
|
|
switch (pAcc->res_type & ResPhysMask) {
|
|
case ResMem:
|
|
pEnt->entityProp |= NEED_MEM;
|
|
break;
|
|
case ResIo:
|
|
pEnt->entityProp |= NEED_IO;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
pAcc = pAcc->next;
|
|
}
|
|
|
|
/*
|
|
* After we have checked all resources of an entity agains any
|
|
* other resource we know if the entity need this resource type
|
|
* (ie. mem/io) at all. if not we can disable this type completely,
|
|
* so no need to share it either.
|
|
*/
|
|
if ((pEnt->entityProp & NEED_MEM_SHARED)
|
|
&& (!(pEnt->entityProp & NEED_MEM)))
|
|
pEnt->entityProp &= ~(unsigned long)NEED_MEM_SHARED;
|
|
|
|
if ((pEnt->entityProp & NEED_IO_SHARED)
|
|
&& (!(pEnt->entityProp & NEED_IO)))
|
|
pEnt->entityProp &= ~(unsigned long)NEED_IO_SHARED;
|
|
}
|
|
|
|
void
|
|
xf86PostPreInit()
|
|
{
|
|
if (doFramebufferMode) return;
|
|
|
|
if (xf86NumScreens > 1)
|
|
needRAC = TRUE;
|
|
|
|
xf86MsgVerb(X_INFO, 3, "do I need RAC?");
|
|
|
|
if (needRAC) {
|
|
xf86ErrorFVerb(3, " Yes, I do.\n");
|
|
} else {
|
|
xf86ErrorFVerb(3, " No, I don't.\n");
|
|
}
|
|
|
|
xf86MsgVerb(X_INFO, 3, "resource ranges after preInit:\n");
|
|
xf86PrintResList(3, Acc);
|
|
}
|
|
|
|
void
|
|
xf86PostScreenInit(void)
|
|
{
|
|
int i,j;
|
|
ScreenPtr pScreen;
|
|
unsigned int flags;
|
|
int nummem = 0, numio = 0;
|
|
|
|
if (doFramebufferMode) {
|
|
SetSIGIOForState(OPERATING);
|
|
return;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
ErrorF("PostScreenInit generation: %i\n",serverGeneration);
|
|
#endif
|
|
if (serverGeneration == 1) {
|
|
checkRoutingForScreens(OPERATING);
|
|
for (i=0; i<xf86NumEntities; i++) {
|
|
checkRequiredResources(i);
|
|
}
|
|
|
|
/*
|
|
* after removing NEED_XXX_SHARED from entities that
|
|
* don't need need XXX resources at all we might have
|
|
* a single entity left that has NEED_XXX_SHARED set.
|
|
* In this case we can delete that, too.
|
|
*/
|
|
for (i = 0; i < xf86NumEntities; i++) {
|
|
if (xf86Entities[i]->entityProp & NEED_MEM_SHARED)
|
|
nummem++;
|
|
if (xf86Entities[i]->entityProp & NEED_IO_SHARED)
|
|
numio++;
|
|
}
|
|
for (i = 0; i < xf86NumEntities; i++) {
|
|
if (nummem < 2)
|
|
xf86Entities[i]->entityProp &= ~NEED_MEM_SHARED;
|
|
if (numio < 2)
|
|
xf86Entities[i]->entityProp &= ~NEED_IO_SHARED;
|
|
}
|
|
}
|
|
|
|
if (xf86Screens && needRAC) {
|
|
int needRACforVga = 0;
|
|
|
|
for (i = 0; i < xf86NumScreens; i++) {
|
|
for (j = 0; j < xf86Screens[i]->numEntities; j++) {
|
|
if (xf86Entities[xf86Screens[i]->entityList[j]]->entityProp
|
|
& NEED_VGA_ROUTED) {
|
|
needRACforVga ++;
|
|
break; /* only count each screen once */
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < xf86NumScreens; i++) {
|
|
Bool needRACforMem = FALSE, needRACforIo = FALSE;
|
|
|
|
for (j = 0; j < xf86Screens[i]->numEntities; j++) {
|
|
if (xf86Entities[xf86Screens[i]->entityList[j]]->entityProp
|
|
& NEED_MEM_SHARED)
|
|
needRACforMem = TRUE;
|
|
if (xf86Entities[xf86Screens[i]->entityList[j]]->entityProp
|
|
& NEED_IO_SHARED)
|
|
needRACforIo = TRUE;
|
|
/*
|
|
* We may need RAC although we don't share any resources
|
|
* as we need to route VGA to the correct bus. This can
|
|
* only be done simultaniously for MEM and IO.
|
|
*/
|
|
if (needRACforVga > 1) {
|
|
if (xf86Entities[xf86Screens[i]->entityList[j]]->entityProp
|
|
& NEED_VGA_MEM)
|
|
needRACforMem = TRUE;
|
|
if (xf86Entities[xf86Screens[i]->entityList[j]]->entityProp
|
|
& NEED_VGA_IO)
|
|
needRACforIo = TRUE;
|
|
}
|
|
}
|
|
|
|
pScreen = xf86Screens[i]->pScreen;
|
|
flags = 0;
|
|
if (needRACforMem) {
|
|
flags |= xf86Screens[i]->racMemFlags;
|
|
xf86ErrorFVerb(3, "Screen %d is using RAC for mem\n", i);
|
|
}
|
|
if (needRACforIo) {
|
|
flags |= xf86Screens[i]->racIoFlags;
|
|
xf86ErrorFVerb(3, "Screen %d is using RAC for io\n", i);
|
|
}
|
|
|
|
xf86RACInit(pScreen,flags);
|
|
}
|
|
}
|
|
|
|
xf86EnterServerState(OPERATING);
|
|
|
|
}
|
|
|
|
/*
|
|
* Sets
|
|
*/
|
|
|
|
|
|
static resPtr
|
|
decomposeSparse(resRange range)
|
|
{
|
|
resRange new;
|
|
resPtr ret = NULL;
|
|
memType val = range.rBegin;
|
|
int i = 0;
|
|
|
|
new.type = (range.type & ~ResExtMask) | ResSparse;
|
|
|
|
while (1) {
|
|
if (val & 0x01) {
|
|
new.rBase = (val << i);
|
|
new.rMask = ~((1 << i) - 1);
|
|
ret = xf86AddResToList(ret,&new,-1);
|
|
val ++;
|
|
}
|
|
i++;
|
|
val >>= 1;
|
|
if ((((val + 1) << i) - 1) > range.rEnd)
|
|
break;
|
|
}
|
|
i--;
|
|
val <<= 1;
|
|
|
|
while (1) {
|
|
if((((val + 1) << i) - 1)> range.rEnd) {
|
|
if (--i < 0) break;
|
|
val <<= 1;
|
|
} else {
|
|
new.rBase = (val << i);
|
|
new.rMask = ~((1 << i) - 1);
|
|
val++;
|
|
ret = xf86AddResToList(ret,&new,-1);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static Bool
|
|
x_isSubsetOf(resRange range, resPtr list1, resPtr list2)
|
|
{
|
|
resRange range1, range2;
|
|
memType m1_A_m2;
|
|
Bool ret;
|
|
resPtr list;
|
|
|
|
if (list1) {
|
|
list = list1;
|
|
if ((range.type & ResTypeMask) == (list->res_type & ResTypeMask)) {
|
|
switch (range.type & ResExtMask) {
|
|
case ResBlock:
|
|
if ((list->res_type & ResExtMask) == ResBlock) {
|
|
if (range.rBegin >= list->block_begin
|
|
&& range.rEnd <= list->block_end)
|
|
return TRUE;
|
|
else if (range.rBegin < list->block_begin
|
|
&& range.rEnd > list->block_end) {
|
|
RANGE(range1, range.rBegin, list->block_begin - 1,
|
|
range.type);
|
|
RANGE(range2, list->block_end + 1, range.rEnd,
|
|
range.type);
|
|
return (x_isSubsetOf(range1,list->next,list2) &&
|
|
x_isSubsetOf(range2,list->next,list2));
|
|
}
|
|
else if (range.rBegin >= list->block_begin
|
|
&& range.rBegin <= list->block_end) {
|
|
RANGE(range1, list->block_end + 1, range.rEnd,
|
|
range.type);
|
|
return (x_isSubsetOf(range1,list->next,list2));
|
|
} else if (range.rEnd >= list->block_begin
|
|
&& range.rEnd <= list->block_end) {
|
|
RANGE(range1,range.rBegin, list->block_begin - 1,
|
|
range.type);
|
|
return (x_isSubsetOf(range1,list->next,list2));
|
|
}
|
|
}
|
|
break;
|
|
case ResSparse:
|
|
if ((list->res_type & ResExtMask) == ResSparse) {
|
|
memType test;
|
|
int i;
|
|
|
|
m1_A_m2 = range.rMask & list->sparse_mask;
|
|
if ((range.rBase ^ list->sparse_base) & m1_A_m2)
|
|
break;
|
|
/*
|
|
* We use the following system:
|
|
* let 0 ^= mask:1 base:0, 1 ^= mask:1 base:1,
|
|
* X mask:0 ; S: set TSS: test set for subset
|
|
* NTSS: new test set after test
|
|
* S: 1 0 1 0 X X 0 1 X
|
|
* TSS: 1 0 0 1 1 0 X X X
|
|
* T: 0 0 1 1 0 0 0 0 0
|
|
* NTSS: 1 0 0/X 1/X 1 0 1 0 X
|
|
* R: 0 0 0 0 0 0 1 1 0
|
|
* If R != 0 TSS and S are disjunct
|
|
* If R == 0 TSS is subset of S
|
|
* If R != 0 NTSS contains elements from TSS
|
|
* which are not also members of S.
|
|
* If a T is set one of the correspondig bits
|
|
* in NTSS must be set to the specified value
|
|
* all other are X
|
|
*/
|
|
test = list->sparse_mask & ~range.rMask;
|
|
if (test == 0)
|
|
return TRUE;
|
|
for (i = 0; i < sizeof(memType); i++) {
|
|
if ((test >> i) & 0x1) {
|
|
RANGE(range1, ((range.rBase & list->sparse_base)
|
|
| (range.rBase & ~list->sparse_mask)
|
|
| ((~list->sparse_base & list->sparse_mask)
|
|
& ~range.rMask)) & range1.rMask,
|
|
((range.rMask | list->sparse_mask) & ~test)
|
|
| (1 << i), range.type);
|
|
return (x_isSubsetOf(range1,list->next,list2));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return (x_isSubsetOf(range,list->next,list2));
|
|
} else if (list2) {
|
|
resPtr tmpList = NULL;
|
|
switch (range.type & ResExtMask) {
|
|
case ResBlock:
|
|
tmpList = decomposeSparse(range);
|
|
while (tmpList) {
|
|
if (!x_isSubsetOf(tmpList->val,list2,NULL)) {
|
|
xf86FreeResList(tmpList);
|
|
return FALSE;
|
|
}
|
|
tmpList = tmpList->next;
|
|
}
|
|
xf86FreeResList(tmpList);
|
|
return TRUE;
|
|
break;
|
|
case ResSparse:
|
|
while (list2) {
|
|
tmpList = xf86JoinResLists(tmpList,decomposeSparse(list2->val));
|
|
list2 = list2->next;
|
|
}
|
|
ret = x_isSubsetOf(range,tmpList,NULL);
|
|
xf86FreeResList(tmpList);
|
|
return ret;
|
|
break;
|
|
}
|
|
} else
|
|
return FALSE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
Bool
|
|
xf86IsSubsetOf(resRange range, resPtr list)
|
|
{
|
|
resPtr dup = xf86DupResList(list);
|
|
resPtr r_sp = NULL, r = NULL, tmp = NULL;
|
|
Bool ret = FALSE;
|
|
|
|
while (dup) {
|
|
tmp = dup;
|
|
dup = dup->next;
|
|
switch (tmp->res_type & ResExtMask) {
|
|
case ResBlock:
|
|
tmp->next = r;
|
|
r = tmp;
|
|
break;
|
|
case ResSparse:
|
|
tmp->next = r_sp;
|
|
r_sp = tmp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (range.type & ResExtMask) {
|
|
case ResBlock:
|
|
ret = x_isSubsetOf(range,r,r_sp);
|
|
break;
|
|
case ResSparse:
|
|
ret = x_isSubsetOf(range,r_sp,r);
|
|
break;
|
|
}
|
|
xf86FreeResList(r);
|
|
xf86FreeResList(r_sp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static resPtr
|
|
findIntersect(resRange Range, resPtr list)
|
|
{
|
|
resRange range;
|
|
resPtr new = NULL;
|
|
|
|
while (list) {
|
|
if ((Range.type & ResTypeMask) == (list->res_type & ResTypeMask)) {
|
|
switch (Range.type & ResExtMask) {
|
|
case ResBlock:
|
|
switch (list->res_type & ResExtMask) {
|
|
case ResBlock:
|
|
if (Range.rBegin >= list->block_begin)
|
|
range.rBegin = Range.rBegin;
|
|
else
|
|
range.rBegin = list->block_begin;
|
|
if (Range.rEnd <= list->block_end)
|
|
range.rEnd = Range.rEnd;
|
|
else
|
|
range.rEnd = list->block_end;
|
|
if (range.rEnd > range.rBegin) {
|
|
range.type = Range.type;
|
|
new = xf86AddResToList(new,&range,-1);
|
|
}
|
|
break;
|
|
case ResSparse:
|
|
new = xf86JoinResLists(new,xf86FindIntersectOfLists(new,decomposeSparse(list->val)));
|
|
break;
|
|
}
|
|
break;
|
|
case ResSparse:
|
|
switch (list->res_type & ResExtMask) {
|
|
case ResSparse:
|
|
if (!((~(range.rBase ^ list->sparse_base)
|
|
& (range.rMask & list->sparse_mask)))) {
|
|
RANGE(range, (range.rBase & list->sparse_base)
|
|
| (~range.rMask & list->sparse_base)
|
|
| (~list->sparse_mask & range.rBase),
|
|
range.rMask | list->sparse_mask,
|
|
Range.type);
|
|
new = xf86AddResToList(new,&range,-1);
|
|
}
|
|
break;
|
|
case ResBlock:
|
|
new = xf86JoinResLists(new,xf86FindIntersectOfLists(
|
|
decomposeSparse(range),list));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
list = list->next;
|
|
}
|
|
return new;
|
|
}
|
|
|
|
resPtr
|
|
xf86FindIntersectOfLists(resPtr l1, resPtr l2)
|
|
{
|
|
resPtr ret = NULL;
|
|
|
|
while (l1) {
|
|
ret = xf86JoinResLists(ret,findIntersect(l1->val,l2));
|
|
l1 = l1->next;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#if 0 /* Not used */
|
|
static resPtr
|
|
xf86FindComplement(resRange Range)
|
|
{
|
|
resRange range;
|
|
memType tmp;
|
|
resPtr new = NULL;
|
|
int i;
|
|
|
|
switch (Range.type & ResExtMask) {
|
|
case ResBlock:
|
|
if (Range.rBegin > 0) {
|
|
RANGE(range, 0, Range.rBegin - 1, Range.type);
|
|
new = xf86AddResToList(new,&range,-1);
|
|
}
|
|
if (Range.rEnd < (memType)~0) {
|
|
RANGE(range,Range.rEnd + 1, (memType)~0, Range.type);
|
|
new = xf86AddResToList(new,&range,-1);
|
|
}
|
|
break;
|
|
case ResSparse:
|
|
tmp = Range.rMask;
|
|
for (i = 0; i < sizeof(memType); i++) {
|
|
if (tmp & 0x1) {
|
|
RANGE(range,(~Range.rMask & range.rMask),(1 << i), Range.type);
|
|
new = xf86AddResToList(new,&range,-1);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return new;
|
|
}
|
|
#endif
|
|
|
|
resPtr
|
|
xf86ExtractTypeFromList(resPtr list, unsigned long type)
|
|
{
|
|
resPtr ret = NULL;
|
|
|
|
while (list) {
|
|
if ((list->res_type & ResTypeMask) == type)
|
|
ret = xf86AddResToList(ret,&(list->val),list->entityIndex);
|
|
list = list->next;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* xf86FindPrimaryDevice() - Find the display device which
|
|
* was active when the server was started.
|
|
*/
|
|
void
|
|
xf86FindPrimaryDevice()
|
|
{
|
|
if (primaryBus.type != BUS_NONE) {
|
|
char *bus;
|
|
char loc[16];
|
|
|
|
switch (primaryBus.type) {
|
|
case BUS_PCI:
|
|
bus = "PCI";
|
|
snprintf(loc, sizeof(loc), " %2.2x@%2.2x:%2.2x:%1.1x",
|
|
primaryBus.id.pci->bus,
|
|
primaryBus.id.pci->domain,
|
|
primaryBus.id.pci->dev,
|
|
primaryBus.id.pci->func);
|
|
break;
|
|
case BUS_SBUS:
|
|
bus = "SBUS";
|
|
snprintf(loc, sizeof(loc), " %2.2x", primaryBus.id.sbus.fbNum);
|
|
break;
|
|
default:
|
|
bus = "";
|
|
loc[0] = '\0';
|
|
}
|
|
|
|
xf86MsgVerb(X_INFO, 2, "Primary Device is: %s%s\n",bus,loc);
|
|
}
|
|
}
|
|
|
|
Bool
|
|
xf86NoSharedResources(int screenIndex,resType res)
|
|
{
|
|
int j;
|
|
|
|
if (screenIndex > xf86NumScreens)
|
|
return TRUE;
|
|
|
|
for (j = 0; j < xf86Screens[screenIndex]->numEntities; j++) {
|
|
switch (res) {
|
|
case IO:
|
|
if ( xf86Entities[xf86Screens[screenIndex]->entityList[j]]->entityProp
|
|
& NEED_IO_SHARED)
|
|
return FALSE;
|
|
break;
|
|
case MEM:
|
|
if ( xf86Entities[xf86Screens[screenIndex]->entityList[j]]->entityProp
|
|
& NEED_MEM_SHARED)
|
|
return FALSE;
|
|
break;
|
|
case MEM_IO:
|
|
if ( xf86Entities[xf86Screens[screenIndex]->entityList[j]]->entityProp
|
|
& NEED_SHARED)
|
|
return FALSE;
|
|
break;
|
|
case NONE:
|
|
break;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
xf86RegisterStateChangeNotificationCallback(xf86StateChangeNotificationCallbackFunc func, pointer arg)
|
|
{
|
|
StateChangeNotificationPtr ptr =
|
|
(StateChangeNotificationPtr)xnfalloc(sizeof(StateChangeNotificationRec));
|
|
|
|
ptr->func = func;
|
|
ptr->arg = arg;
|
|
ptr->next = StateChangeNotificationList;
|
|
StateChangeNotificationList = ptr;
|
|
}
|
|
|
|
Bool
|
|
xf86DeregisterStateChangeNotificationCallback(xf86StateChangeNotificationCallbackFunc func)
|
|
{
|
|
StateChangeNotificationPtr *ptr = &StateChangeNotificationList;
|
|
StateChangeNotificationPtr tmp;
|
|
|
|
while (*ptr) {
|
|
if ((*ptr)->func == func) {
|
|
tmp = (*ptr);
|
|
(*ptr) = (*ptr)->next;
|
|
xfree(tmp);
|
|
return TRUE;
|
|
}
|
|
ptr = &((*ptr)->next);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
notifyStateChange(xf86NotifyState state)
|
|
{
|
|
StateChangeNotificationPtr ptr = StateChangeNotificationList;
|
|
while (ptr) {
|
|
ptr->func(state,ptr->arg);
|
|
ptr = ptr->next;
|
|
}
|
|
}
|
|
|
|
/* Multihead accel sharing accessor functions and entity Private handling */
|
|
|
|
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 */
|
|
bzero(&nprivs[idx], 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]);
|
|
}
|
|
|