215 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			215 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
 | 
						|
#ifdef HAVE_XORG_CONFIG_H
 | 
						|
#include <xorg-config.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include <stdio.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 bus;
 | 
						|
    unsigned int devfn;
 | 
						|
    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;
 | 
						|
    struct pci_dev *tmp, *ret = NULL;
 | 
						|
    unsigned int num;
 | 
						|
    char *res;
 | 
						|
    
 | 
						|
    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,&tmp->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]);
 | 
						|
            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)
 | 
						|
{
 | 
						|
    unsigned int dev, fn;
 | 
						|
    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) {
 | 
						|
        dev = device->devfn >> 3;
 | 
						|
	fn = device->devfn & 0x7;
 | 
						|
        if (tag == pciTag(device->bus,dev,fn)) {
 | 
						|
            *bits = 0;
 | 
						|
            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;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/* Query the kvirt address (64bit) of a BAR range from TAG */
 | 
						|
Bool
 | 
						|
xf86GetPciOffsetFromOS(PCITAG tag, int index, unsigned long* bases)
 | 
						|
{
 | 
						|
    unsigned int dev, fn;
 | 
						|
    struct pci_dev *device;
 | 
						|
 | 
						|
    if (index > 7)
 | 
						|
        return FALSE;
 | 
						|
 | 
						|
    if (!xf86OSLinuxPCIDevs) {
 | 
						|
        xf86OSLinuxPCIDevs = xf86OSLinuxGetPciDevs();
 | 
						|
    }
 | 
						|
    if (!xf86OSLinuxPCIDevs)
 | 
						|
        return FALSE;
 | 
						|
 | 
						|
    for (device = xf86OSLinuxPCIDevs; device; device = device->next) {
 | 
						|
        dev = device->devfn >> 3;
 | 
						|
        fn = device->devfn & 0x7;
 | 
						|
        if (tag == pciTag(device->bus,dev,fn)) {
 | 
						|
            /* return the offset for the index requested */
 | 
						|
            *bases = device->offset[index];
 | 
						|
            return TRUE;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/* 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 dev, fn;
 | 
						|
    unsigned int ndx;
 | 
						|
    struct pci_dev *device;
 | 
						|
 | 
						|
    if (!xf86OSLinuxPCIDevs) {
 | 
						|
        xf86OSLinuxPCIDevs = xf86OSLinuxGetPciDevs();
 | 
						|
    }
 | 
						|
    if (!xf86OSLinuxPCIDevs) {
 | 
						|
        return FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
    for (device = xf86OSLinuxPCIDevs; device; device = device->next) {
 | 
						|
        dev = device->devfn >> 3;
 | 
						|
        fn = device->devfn & 0x7;
 | 
						|
        if (tag == pciTag(device->bus, dev, 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;
 | 
						|
}
 |