xenocara/xserver/hw/xfree86/os-support/linux/lnx_pci.c

259 lines
7.0 KiB
C
Raw Normal View History

2006-11-26 11:13:41 -07:00
#ifdef HAVE_XORG_CONFIG_H
#include <xorg-config.h>
#endif
#include <sys/types.h>
#include <stdio.h>
#include <dirent.h>
#include <X11/X.h>
#include "os.h"
#include "xf86.h"
#include "xf86Priv.h"
#define XF86_OS_PRIVS
#include "xf86_OSproc.h"
#include "xf86Pci.h"
#ifdef __sparc__
#define PCIADDR_TYPE long long
#define PCIADDR_IGNORE_FMT "%*x"
#define PCIADDR_FMT "%llx"
#else
#define PCIADDR_TYPE long
#define PCIADDR_IGNORE_FMT "%*x"
#define PCIADDR_FMT "%lx"
#endif
int lnxPciInit(void);
struct pci_dev {
unsigned int domain;
unsigned int bus;
unsigned int dev;
unsigned int fn;
PCIADDR_TYPE offset[7];
PCIADDR_TYPE size[7];
struct pci_dev *next;
};
struct pci_dev *xf86OSLinuxPCIDevs = NULL;
int xf86OSLinuxNumPciDevs = 0;
static struct pci_dev *xf86OSLinuxGetPciDevs(void) {
char c[0x200];
FILE *file = NULL;
DIR *dir;
struct dirent *dirent;
struct pci_dev *tmp, *ret = NULL;
unsigned int i, num, devfn;
unsigned PCIADDR_TYPE begin, end;
char *res;
/* Try 2.6 devices first, with domain support */
if ( (dir = opendir ("/sys/bus/pci/devices")) ) {
xf86OSLinuxNumPciDevs = 0;
while ( (dirent = readdir (dir)) ) {
unsigned int domain, bus, dev, fn;
if (sscanf (dirent->d_name, "%04x:%02x:%02x.%01x",
&domain, &bus, &dev, &fn) == 4) {
tmp = xcalloc (sizeof(struct pci_dev), 1);
tmp->domain = domain;
tmp->bus = bus;
tmp->dev = dev;
tmp->fn = fn;
sprintf (c, "/sys/bus/pci/devices/%12s/resource",
dirent->d_name);
i = 0;
if ( (file = fopen (c, "r")) ) {
while (i < 7 && fgets (c, 0x200, file)) {
if (sscanf (c, PCIADDR_FMT " " PCIADDR_FMT " "
PCIADDR_IGNORE_FMT, &begin, &end) == 2) {
tmp->offset[i] = begin;
tmp->size[i] = begin ? end-begin+1 : 0;
i++;
}
}
fclose (file);
}
if (i > 0) {
tmp->next = ret;
ret = tmp;
xf86OSLinuxNumPciDevs++;
} else
xfree (tmp);
}
}
closedir (dir);
}
if (ret)
return ret;
file = fopen("/proc/bus/pci/devices", "r");
if (!file) return NULL;
xf86OSLinuxNumPciDevs = 0;
do {
res = fgets(c, 0x1ff, file);
if (res) {
tmp = xcalloc(sizeof(struct pci_dev),1);
num = sscanf(res,
/*bus+dev vendorid deviceid irq */
"%02x%02x\t%*04x%*04x\t%*x"
/* 7 PCI resource base addresses */
"\t" PCIADDR_FMT
"\t" PCIADDR_FMT
"\t" PCIADDR_FMT
"\t" PCIADDR_FMT
"\t" PCIADDR_FMT
"\t" PCIADDR_FMT
"\t" PCIADDR_FMT
/* 7 PCI resource sizes, and then optionally a driver name */
"\t" PCIADDR_FMT
"\t" PCIADDR_FMT
"\t" PCIADDR_FMT
"\t" PCIADDR_FMT
"\t" PCIADDR_FMT
"\t" PCIADDR_FMT
"\t" PCIADDR_FMT,
&tmp->bus,&devfn,&tmp->offset[0],&tmp->offset[1],&tmp->offset[2],&tmp->offset[3],
&tmp->offset[4],&tmp->offset[5],&tmp->offset[6], &tmp->size[0], &tmp->size[1], &tmp->size[2],
&tmp->size[3], &tmp->size[4], &tmp->size[5], &tmp->size[6]);
tmp->dev = devfn >> 3;
tmp->fn = devfn & 0x7;
if (num != 16) { /* apparantly not 2.3 style */
xfree(tmp);
fclose(file);
return NULL;
}
if (ret) {
tmp->next = ret;
}
ret = tmp;
xf86OSLinuxNumPciDevs++;
}
} while (res);
fclose(file);
return ret;
}
/* not to be confused with linuxPciInit (i.e. ARCH_PCI_INIT), found in
* os-support/bus/linuxPci.c. */
int lnxPciInit(void) {
if (!xf86OSLinuxPCIDevs)
xf86OSLinuxPCIDevs = xf86OSLinuxGetPciDevs();
return xf86OSLinuxNumPciDevs;
}
Bool
xf86GetPciSizeFromOS(PCITAG tag, int index, int* bits)
{
signed PCIADDR_TYPE Size;
struct pci_dev *device;
if (index >= 7)
return FALSE;
if (!xf86OSLinuxPCIDevs) {
xf86OSLinuxPCIDevs = xf86OSLinuxGetPciDevs();
}
if (!xf86OSLinuxPCIDevs)
return FALSE;
for (device = xf86OSLinuxPCIDevs; device; device = device->next) {
if (tag == pciDomTag (device->domain, device->bus,
device->dev, device->fn)) {
if (device->size[index] != 0) {
Size = device->size[index] - ((PCIADDR_TYPE) 1);
while (Size & ((PCIADDR_TYPE) 0x01)) {
Size = Size >> ((PCIADDR_TYPE) 1);
(*bits)++;
}
}
return TRUE;
}
}
return FALSE;
}
#if 0
/* Query the kvirt address (64bit) of a BAR range from TAG */
Bool
xf86GetPciOffsetFromOS(PCITAG tag, int index, unsigned long* bases)
{
struct pci_dev *device;
if (index >= 7)
return FALSE;
if (!xf86OSLinuxPCIDevs) {
xf86OSLinuxPCIDevs = xf86OSLinuxGetPciDevs();
}
if (!xf86OSLinuxPCIDevs)
return FALSE;
for (device = xf86OSLinuxPCIDevs; device; device = device->next) {
if (tag == pciDomTag (device->domain, device->bus,
device->dev, device->fn)) {
/* return the offset for the index requested */
*bases = device->offset[index];
return TRUE;
}
}
return FALSE;
}
#endif
/* Query the kvirt address (64bit) of a BAR range from size for a given TAG */
unsigned long
xf86GetOSOffsetFromPCI(PCITAG tag, int space, unsigned long base)
{
unsigned int ndx;
struct pci_dev *device;
if (!xf86OSLinuxPCIDevs) {
xf86OSLinuxPCIDevs = xf86OSLinuxGetPciDevs();
}
if (!xf86OSLinuxPCIDevs) {
return FALSE;
}
for (device = xf86OSLinuxPCIDevs; device; device = device->next) {
if (tag == pciDomTag (device->domain, device->bus,
device->dev, device->fn)) {
/* ok now look through all the BAR values of this device */
pciConfigPtr pDev = xf86GetPciConfigFromTag(tag);
for (ndx=0; ndx<7; ndx++) {
unsigned long savePtr, flagMask;
if (ndx == 6)
savePtr = pDev->pci_baserom;
else /* this the ROM bar */
savePtr = (&pDev->pci_base0)[ndx];
/* Ignore unset base addresses. The kernel may
* have reported non-zero size and address even
* if they are disabled (e.g. disabled ROM BAR).
*/
if (savePtr == 0)
continue;
/* Remove memory attribute bits, different for IO
* and memory ranges. */
flagMask = (savePtr & 0x1) ? ~0x3UL : ~0xFUL;
savePtr &= flagMask;
/* find the index of the incoming base */
if (base >= savePtr && base < (savePtr + device->size[ndx])) {
return (device->offset[ndx] & flagMask) + (base - savePtr);
}
}
}
};
return 0;
}