1084 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1084 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright 1998 by Concurrent Computer Corporation
 | |
|  *
 | |
|  * Permission to use, copy, modify, distribute, and sell this software
 | |
|  * and its documentation for any purpose is hereby granted without fee,
 | |
|  * provided that the above copyright notice appear in all copies and that
 | |
|  * both that copyright notice and this permission notice appear in
 | |
|  * supporting documentation, and that the name of Concurrent Computer
 | |
|  * Corporation not be used in advertising or publicity pertaining to
 | |
|  * distribution of the software without specific, written prior
 | |
|  * permission.  Concurrent Computer Corporation makes no representations
 | |
|  * about the suitability of this software for any purpose.  It is
 | |
|  * provided "as is" without express or implied warranty.
 | |
|  *
 | |
|  * CONCURRENT COMPUTER CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD
 | |
|  * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 | |
|  * AND FITNESS, IN NO EVENT SHALL CONCURRENT COMPUTER CORPORATION BE
 | |
|  * LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
 | |
|  * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 | |
|  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 | |
|  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 | |
|  * SOFTWARE.
 | |
|  *
 | |
|  * Copyright 1998 by Metro Link Incorporated
 | |
|  *
 | |
|  * Permission to use, copy, modify, distribute, and sell this software
 | |
|  * and its documentation for any purpose is hereby granted without fee,
 | |
|  * provided that the above copyright notice appear in all copies and that
 | |
|  * both that copyright notice and this permission notice appear in
 | |
|  * supporting documentation, and that the name of Metro Link
 | |
|  * Incorporated not be used in advertising or publicity pertaining to
 | |
|  * distribution of the software without specific, written prior
 | |
|  * permission.  Metro Link Incorporated makes no representations
 | |
|  * about the suitability of this software for any purpose.  It is
 | |
|  * provided "as is" without express or implied warranty.
 | |
|  *
 | |
|  * METRO LINK INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD
 | |
|  * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 | |
|  * AND FITNESS, IN NO EVENT SHALL METRO LINK INCORPORATED BE
 | |
|  * LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
 | |
|  * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 | |
|  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 | |
|  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 | |
|  * SOFTWARE.
 | |
|  */
 | |
| 
 | |
| #ifdef HAVE_XORG_CONFIG_H
 | |
| #include <xorg-config.h>
 | |
| #endif
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include "compiler.h"
 | |
| #include "xf86.h"
 | |
| #include "xf86Priv.h"
 | |
| #include "xf86_OSlib.h"
 | |
| #include "Pci.h"
 | |
| #include <dirent.h>
 | |
| 
 | |
| /*
 | |
|  * linux platform specific PCI access functions -- using /proc/bus/pci
 | |
|  * needs kernel version 2.2.x
 | |
|  */
 | |
| static CARD32 linuxPciCfgRead(PCITAG tag, int off);
 | |
| static void linuxPciCfgWrite(PCITAG, int off, CARD32 val);
 | |
| static void linuxPciCfgSetBits(PCITAG tag, int off, CARD32 mask, CARD32 bits);
 | |
| static ADDRESS linuxTransAddrBusToHost(PCITAG tag, PciAddrType type, ADDRESS addr);
 | |
| #if defined(__powerpc__)
 | |
| static ADDRESS linuxPpcBusAddrToHostAddr(PCITAG, PciAddrType, ADDRESS);
 | |
| static ADDRESS linuxPpcHostAddrToBusAddr(PCITAG, PciAddrType, ADDRESS);
 | |
| #endif
 | |
| 
 | |
| static CARD8 linuxPciCfgReadByte(PCITAG tag, int off);
 | |
| static void linuxPciCfgWriteByte(PCITAG tag, int off, CARD8 val);
 | |
| static CARD16 linuxPciCfgReadWord(PCITAG tag, int off);
 | |
| static void linuxPciCfgWriteWord(PCITAG tag, int off, CARD16 val);
 | |
| static int linuxPciHandleBIOS(PCITAG Tag, int basereg, unsigned char *buf, int len);
 | |
| static Bool linuxDomainSupport(void);
 | |
| 
 | |
| static pciBusFuncs_t linuxFuncs0 = {
 | |
| /* pciReadLong      */	linuxPciCfgRead,
 | |
| /* pciWriteLong     */	linuxPciCfgWrite,
 | |
| /* pciSetBitsLong   */	linuxPciCfgSetBits,
 | |
| #if defined(__powerpc__)
 | |
| /* pciAddrHostToBus */	linuxPpcHostAddrToBusAddr,
 | |
| /* pciAddrBusToHost */	linuxPpcBusAddrToHostAddr,
 | |
| #else
 | |
| /* pciAddrHostToBus */	pciAddrNOOP,
 | |
| /* linuxTransAddrBusToHost is busted on sparc64 but the PCI rework tree
 | |
|  * makes it all moot, so we kludge it for now */
 | |
| #if defined(__sparc__)
 | |
| /* pciAddrBusToHost */  pciAddrNOOP,
 | |
| #else
 | |
| /* pciAddrBusToHost */	linuxTransAddrBusToHost,
 | |
| #endif /* __sparc64__ */
 | |
| #endif
 | |
| 
 | |
| /* pciControlBridge */		NULL,
 | |
| /* pciGetBridgeBuses */		NULL,
 | |
| /* pciGetBridgeResources */	NULL,
 | |
| 
 | |
| /* pciReadByte */	linuxPciCfgReadByte,
 | |
| /* pciWriteByte */	linuxPciCfgWriteByte,
 | |
| 
 | |
| /* pciReadWord */	linuxPciCfgReadWord,
 | |
| /* pciWriteWord */	linuxPciCfgWriteWord,
 | |
| };
 | |
| 
 | |
| static pciBusInfo_t linuxPci0 = {
 | |
| /* configMech  */	PCI_CFG_MECH_OTHER,
 | |
| /* numDevices  */	32,
 | |
| /* secondary   */	FALSE,
 | |
| /* primary_bus */	0,
 | |
| /* funcs       */	&linuxFuncs0,
 | |
| /* pciBusPriv  */	NULL,
 | |
| /* bridge      */	NULL
 | |
| };
 | |
| 
 | |
| /* from lnx_pci.c. */
 | |
| extern int lnxPciInit(void);
 | |
| 
 | |
| static Bool	domain_support = FALSE;
 | |
| 
 | |
| void
 | |
| linuxPciInit()
 | |
| {
 | |
| 	struct stat st;
 | |
| 	if ((xf86Info.pciFlags == PCIForceNone) ||
 | |
| 	    (-1 == stat("/proc/bus/pci", &st))) {
 | |
| 		/* when using this as default for all linux architectures,
 | |
| 		   we'll need a fallback for 2.0 kernels here */
 | |
| 		return;
 | |
| 	}
 | |
| #ifndef INCLUDE_XF86_NO_DOMAIN
 | |
| 	domain_support = linuxDomainSupport();
 | |
| #endif
 | |
| 	pciNumBuses    = 1;
 | |
| 	pciBusInfo[0]  = &linuxPci0;
 | |
| 	pciFindFirstFP = pciGenFindFirst;
 | |
| 	pciFindNextFP  = pciGenFindNext;
 | |
| 	pciSetOSBIOSPtr(linuxPciHandleBIOS);
 | |
|         xf86MaxPciDevs = lnxPciInit();
 | |
| }
 | |
| 
 | |
| static int
 | |
| linuxPciOpenFile(PCITAG tag, Bool write)
 | |
| {
 | |
| 	static int	ldomain, lbus,ldev,lfunc,fd = -1,is_write = 0;
 | |
| 	int		domain, bus, dev, func;
 | |
| 	char		file[64];
 | |
| 	struct stat	ignored;
 | |
| 	static int is26 = -1;
 | |
| 
 | |
| 	domain = PCI_DOM_FROM_TAG(tag);
 | |
| 	bus  = PCI_BUS_NO_DOMAIN(PCI_BUS_FROM_TAG(tag));
 | |
| 	dev  = PCI_DEV_FROM_TAG(tag);
 | |
| 	func = PCI_FUNC_FROM_TAG(tag);
 | |
| 	if (is26 == -1) {
 | |
| 		if (stat("/sys/bus/pci",&ignored) < 0)
 | |
| 			is26 = 0;
 | |
| 		else
 | |
| 			is26 = 1;
 | |
| 	}
 | |
| 	
 | |
| 	if (!domain_support && domain > 0)
 | |
| 	    return -1;
 | |
| 
 | |
| 	if (fd == -1 || (write && (!is_write)) || domain != ldomain
 | |
| 	    || bus != lbus || dev != ldev || func != lfunc) {
 | |
| 		if (fd != -1) {
 | |
| 			close(fd);
 | |
| 			fd = -1;
 | |
| 		}
 | |
| 		if (is26)
 | |
| 			sprintf(file,"/sys/bus/pci/devices/%04x:%02x:%02x.%01x/config",
 | |
| 				domain, bus, dev, func);
 | |
| 		else {
 | |
| 			if (bus < 256) {
 | |
| 				sprintf(file, "/proc/bus/pci/%04x:%02x", domain, bus);
 | |
| 				if (stat(file, &ignored) < 0) {
 | |
| 					if (domain == 0) 
 | |
| 						sprintf(file, "/proc/bus/pci/%02x/%02x.%1x",
 | |
| 							bus, dev, func);
 | |
| 					else
 | |
| 						goto bail;
 | |
| 				} else
 | |
| 					sprintf(file, "/proc/bus/pci/%04x:%02x/%02x.%1x",
 | |
| 						domain, bus, dev, func);
 | |
| 			} else {
 | |
| 				sprintf(file, "/proc/bus/pci/%04x:%04x", domain, bus);
 | |
| 				if (stat(file, &ignored) < 0) {
 | |
| 					if (domain == 0)
 | |
| 						sprintf(file, "/proc/bus/pci/%04x/%02x.%1x",
 | |
| 							bus, dev, func);
 | |
| 					else
 | |
| 						goto bail;
 | |
| 				} else
 | |
| 					sprintf(file, "/proc/bus/pci/%04x:%04x/%02x.%1x",
 | |
| 						domain, bus, dev, func);
 | |
| 			}
 | |
| 		}
 | |
| 		if (write) {
 | |
| 		    fd = open(file,O_RDWR);
 | |
| 		    if (fd != -1) is_write = TRUE;
 | |
| 		} else switch (is_write) {
 | |
| 			case TRUE:
 | |
| 			    fd = open(file,O_RDWR);
 | |
| 			    if (fd > -1)
 | |
| 				break;
 | |
| 			default:
 | |
| 			    fd = open(file,O_RDONLY);
 | |
| 			    is_write = FALSE;
 | |
| 		}
 | |
| 	bail:
 | |
| 		ldomain = domain;
 | |
| 		lbus  = bus;
 | |
| 		ldev  = dev;
 | |
| 		lfunc = func;
 | |
| 	}
 | |
| 	return fd;
 | |
| }
 | |
| 
 | |
| static CARD32
 | |
| linuxPciCfgRead(PCITAG tag, int off)
 | |
| {
 | |
| 	int	fd;
 | |
| 	CARD32	val = 0xffffffff;
 | |
| 
 | |
| 	if (-1 != (fd = linuxPciOpenFile(tag,FALSE))) {
 | |
| 		lseek(fd,off,SEEK_SET);
 | |
| 		read(fd,&val,4);
 | |
| 	}
 | |
| 	return PCI_CPU(val);
 | |
| }
 | |
| 
 | |
| static void
 | |
| linuxPciCfgWrite(PCITAG tag, int off, CARD32 val)
 | |
| {
 | |
| 	int	fd;
 | |
| 
 | |
| 	if (-1 != (fd = linuxPciOpenFile(tag,TRUE))) {
 | |
| 		lseek(fd,off,SEEK_SET);
 | |
| 		val = PCI_CPU(val);
 | |
| 		write(fd,&val,4);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void
 | |
| linuxPciCfgSetBits(PCITAG tag, int off, CARD32 mask, CARD32 bits)
 | |
| {
 | |
| 	int	fd;
 | |
| 	CARD32	val = 0xffffffff;
 | |
| 
 | |
| 	if (-1 != (fd = linuxPciOpenFile(tag,TRUE))) {
 | |
| 		lseek(fd,off,SEEK_SET);
 | |
| 		read(fd,&val,4);
 | |
| 		val = PCI_CPU(val);
 | |
| 		val = (val & ~mask) | (bits & mask);
 | |
| 		val = PCI_CPU(val);
 | |
| 		lseek(fd,off,SEEK_SET);
 | |
| 		write(fd,&val,4);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This function will convert a BAR address into a host address
 | |
|  * suitable for passing into the mmap function of a /proc/bus
 | |
|  * device.
 | |
|  */
 | |
| ADDRESS linuxTransAddrBusToHost(PCITAG tag, PciAddrType type, ADDRESS addr)
 | |
| {
 | |
|     ADDRESS ret = xf86GetOSOffsetFromPCI(tag, PCI_MEM|PCI_IO, addr);
 | |
| 
 | |
|     if (ret)
 | |
| 	return ret;
 | |
| 
 | |
|     /*
 | |
|      * if it is not a BAR address, it must be legacy, (or wrong)
 | |
|      * return it as is..
 | |
|      */
 | |
|     return addr;
 | |
| }
 | |
| 
 | |
| 
 | |
| #if defined(__powerpc__)
 | |
| 
 | |
| #ifndef __NR_pciconfig_iobase
 | |
| #define __NR_pciconfig_iobase   200
 | |
| #endif
 | |
| 
 | |
| static ADDRESS
 | |
| linuxPpcBusAddrToHostAddr(PCITAG tag, PciAddrType type, ADDRESS addr)
 | |
| {
 | |
|     if (type == PCI_MEM)
 | |
|     {
 | |
| 	ADDRESS membase = syscall(__NR_pciconfig_iobase, 1,
 | |
| 		    PCI_BUS_FROM_TAG(tag), PCI_DFN_FROM_TAG(tag));
 | |
| 	return (addr + membase);
 | |
|     }
 | |
|     else if (type == PCI_IO)
 | |
|     {
 | |
| 	ADDRESS iobase = syscall(__NR_pciconfig_iobase, 2,
 | |
| 		    PCI_BUS_FROM_TAG(tag), PCI_DFN_FROM_TAG(tag));
 | |
| 	return (addr + iobase);
 | |
|     }
 | |
|     else return addr;
 | |
| }
 | |
| 
 | |
| static ADDRESS
 | |
| linuxPpcHostAddrToBusAddr(PCITAG tag, PciAddrType type, ADDRESS addr)
 | |
| {
 | |
|     if (type == PCI_MEM)
 | |
|     {
 | |
| 	ADDRESS membase = syscall(__NR_pciconfig_iobase, 1,
 | |
| 		    PCI_BUS_FROM_TAG(tag), PCI_DFN_FROM_TAG(tag));
 | |
| 	return (addr - membase);
 | |
|     }
 | |
|     else if (type == PCI_IO)
 | |
|     {
 | |
| 	ADDRESS iobase = syscall(__NR_pciconfig_iobase, 2,
 | |
| 		    PCI_BUS_FROM_TAG(tag), PCI_DFN_FROM_TAG(tag));
 | |
| 	return (addr - iobase);
 | |
|     }
 | |
|     else return addr;
 | |
| }
 | |
| 
 | |
| #endif /* __powerpc__ */
 | |
| 
 | |
| static CARD8
 | |
| linuxPciCfgReadByte(PCITAG tag, int off)
 | |
| {
 | |
| 	int	fd;
 | |
| 	CARD8	val = 0xff;
 | |
| 
 | |
| 	if (-1 != (fd = linuxPciOpenFile(tag,FALSE))) {
 | |
| 		lseek(fd,off,SEEK_SET);
 | |
| 		read(fd,&val,1);
 | |
| 	}
 | |
| 
 | |
| 	return val;
 | |
| }
 | |
| 
 | |
| static void
 | |
| linuxPciCfgWriteByte(PCITAG tag, int off, CARD8 val)
 | |
| {
 | |
| 	int	fd;
 | |
| 
 | |
| 	if (-1 != (fd = linuxPciOpenFile(tag,TRUE))) {
 | |
| 		lseek(fd,off,SEEK_SET);
 | |
| 		write(fd, &val, 1);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static CARD16
 | |
| linuxPciCfgReadWord(PCITAG tag, int off)
 | |
| {
 | |
| 	int	fd;
 | |
| 	CARD16	val = 0xff;
 | |
| 
 | |
| 	if (-1 != (fd = linuxPciOpenFile(tag,FALSE))) {
 | |
| 		lseek(fd, off, SEEK_SET);
 | |
| 		read(fd, &val, 2);
 | |
| 	}
 | |
| 
 | |
| 	return PCI_CPU16(val);
 | |
| }
 | |
| 
 | |
| static void
 | |
| linuxPciCfgWriteWord(PCITAG tag, int off, CARD16 val)
 | |
| {
 | |
| 	int	fd;
 | |
| 
 | |
| 	if (-1 != (fd = linuxPciOpenFile(tag,TRUE))) {
 | |
| 		lseek(fd, off, SEEK_SET);
 | |
| 		val = PCI_CPU16(val);
 | |
| 		write(fd, &val, 2);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| #ifndef INCLUDE_XF86_NO_DOMAIN
 | |
| 
 | |
| /*
 | |
|  * Compiling the following simply requires the presence of <linux/pci.c>.
 | |
|  * Actually running this is another matter altogether...
 | |
|  *
 | |
|  * This scheme requires that the kernel allow mmap()'ing of a host bridge's I/O
 | |
|  * and memory spaces through its /proc/bus/pci/BUS/DFN entry.  Which one is
 | |
|  * determined by a prior ioctl().
 | |
|  *
 | |
|  * For the sparc64 port, this means 2.4.12 or later.  For ppc, this
 | |
|  * functionality is almost, but not quite there yet.  Alpha and other kernel
 | |
|  * ports to multi-domain architectures still need to implement this.
 | |
|  *
 | |
|  * This scheme is also predicated on the use of an IOADDRESS compatible type to
 | |
|  * designate I/O addresses.  Although IOADDRESS is defined as an unsigned
 | |
|  * integral type, it is actually the virtual address of, i.e. a pointer to, the
 | |
|  * I/O port to access.  And so, the inX/outX macros in "compiler.h" need to be
 | |
|  * #define'd appropriately (as is done on SPARC's).
 | |
|  *
 | |
|  * Another requirement to port this scheme to another multi-domain architecture
 | |
|  * is to add the appropriate entries in the pciControllerSizes array below.
 | |
|  *
 | |
|  * TO DO:  Address the deleterious reaction some host bridges have to master
 | |
|  *         aborts.  This is already done for secondary PCI buses, but not yet
 | |
|  *         for accesses to primary buses (except for the SPARC port, where
 | |
|  *         master aborts are avoided during PCI scans).
 | |
|  */
 | |
| 
 | |
| #include <linux/pci.h>
 | |
| 
 | |
| #ifndef PCIIOC_BASE		/* Ioctls for /proc/bus/pci/X/Y nodes. */
 | |
| #define PCIIOC_BASE		('P' << 24 | 'C' << 16 | 'I' << 8)
 | |
| 
 | |
| /* Get controller for PCI device. */
 | |
| #define PCIIOC_CONTROLLER	(PCIIOC_BASE | 0x00)
 | |
| /* Set mmap state to I/O space. */
 | |
| #define PCIIOC_MMAP_IS_IO	(PCIIOC_BASE | 0x01)
 | |
| /* Set mmap state to MEM space. */
 | |
| #define PCIIOC_MMAP_IS_MEM	(PCIIOC_BASE | 0x02)
 | |
| /* Enable/disable write-combining. */
 | |
| #define PCIIOC_WRITE_COMBINE	(PCIIOC_BASE | 0x03)
 | |
| 
 | |
| #endif
 | |
| 
 | |
| /* This probably shouldn't be Linux-specific */
 | |
| static pciConfigPtr
 | |
| xf86GetPciHostConfigFromTag(PCITAG Tag)
 | |
| {
 | |
|     int bus = PCI_BUS_FROM_TAG(Tag);
 | |
|     pciBusInfo_t *pBusInfo;
 | |
| 
 | |
|     while ((bus < pciNumBuses) && (pBusInfo = pciBusInfo[bus])) {
 | |
| 	if (bus == pBusInfo->primary_bus)
 | |
| 	    return pBusInfo->bridge;
 | |
| 	bus = pBusInfo->primary_bus;
 | |
|     }
 | |
| 
 | |
|     return NULL;	/* Bad data */
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This is ugly, but until I can extract this information from the kernel,
 | |
|  * it'll have to do.  The default I/O space size is 64K, and 4G for memory.
 | |
|  * Anything else needs to go in this table.  (PowerPC folk take note.)
 | |
|  *
 | |
|  * Note that Linux/SPARC userland is 32-bit, so 4G overflows to zero here.
 | |
|  *
 | |
|  * Please keep this table in ascending vendor/device order.
 | |
|  */
 | |
| static const struct pciSizes {
 | |
|     unsigned short vendor, device;
 | |
|     unsigned long io_size, mem_size;
 | |
| } pciControllerSizes[] = {
 | |
|     {
 | |
| 	PCI_VENDOR_SUN, PCI_CHIP_PSYCHO,
 | |
| 	1U << 16, 1U << 31
 | |
|     },
 | |
|     {
 | |
| 	PCI_VENDOR_SUN, PCI_CHIP_SCHIZO,
 | |
| 	1U << 24, 1U << 31	/* ??? */
 | |
|     },
 | |
|     {
 | |
| 	PCI_VENDOR_SUN, PCI_CHIP_SABRE,
 | |
| 	1U << 24, (unsigned long)(1ULL << 32)
 | |
|     },
 | |
|     {
 | |
| 	PCI_VENDOR_SUN, PCI_CHIP_HUMMINGBIRD,
 | |
| 	1U << 24, (unsigned long)(1ULL << 32)
 | |
|     }
 | |
| };
 | |
| #define NUM_SIZES (sizeof(pciControllerSizes) / sizeof(pciControllerSizes[0]))
 | |
| 
 | |
| static const struct pciSizes *
 | |
| linuxGetSizesStruct(PCITAG Tag)
 | |
| {
 | |
|     static const struct pciSizes default_size = {
 | |
| 	0, 0, 1U << 16, (unsigned long)(1ULL << 32)
 | |
|     };
 | |
|     pciConfigPtr pPCI;
 | |
|     int          i;
 | |
| 
 | |
|     /* Find host bridge */
 | |
|     if ((pPCI = xf86GetPciHostConfigFromTag(Tag))) {
 | |
| 	/* Look up vendor/device */
 | |
| 	for (i = 0;  i < NUM_SIZES;  i++) {
 | |
| 	    if ((pPCI->pci_vendor == pciControllerSizes[i].vendor)
 | |
| 		&& (pPCI->pci_device == pciControllerSizes[i].device)) {
 | |
| 		return & pciControllerSizes[i];
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     /* Default to 64KB I/O and 4GB memory. */
 | |
|     return & default_size;
 | |
| }
 | |
| 
 | |
| static __inline__ unsigned long
 | |
| linuxGetIOSize(PCITAG Tag)
 | |
| {
 | |
|     const struct pciSizes * const sizes = linuxGetSizesStruct(Tag);
 | |
|     return sizes->io_size;
 | |
| }
 | |
| 
 | |
| static __inline__ void
 | |
| linuxGetSizes(PCITAG Tag, unsigned long *io_size, unsigned long *mem_size)
 | |
| {
 | |
|     const struct pciSizes * const sizes = linuxGetSizesStruct(Tag);
 | |
| 
 | |
|     *io_size  = sizes->io_size;
 | |
|     *mem_size = sizes->mem_size;
 | |
| }
 | |
| 
 | |
| static Bool
 | |
| linuxDomainSupport(void)
 | |
| {
 | |
|     DIR *dir;
 | |
|     struct dirent *dirent;
 | |
|     char *end;
 | |
| 
 | |
|     if (!(dir = opendir("/proc/bus/pci")))
 | |
|        return FALSE;
 | |
|     while (1) {
 | |
| 	if (!(dirent = readdir(dir)))
 | |
| 	    return FALSE;
 | |
| 	strtol(dirent->d_name,&end,16);
 | |
| 	/* entry of the form xx or xxxx : x=[0..f] no domain */
 | |
| 	if (*end == '\0')
 | |
| 	    return FALSE;
 | |
| 	else if (*end == ':') {
 | |
| 	    /* ':' found immediately after: verify for xxxx:xx or xxxx:xxxx */
 | |
| 	    strtol(end + 1,&end,16);
 | |
| 	    if (*end == '\0')
 | |
| 		return TRUE;
 | |
| 	}
 | |
|     }
 | |
|     return FALSE;
 | |
| } 
 | |
| 
 | |
| _X_EXPORT int
 | |
| xf86GetPciDomain(PCITAG Tag)
 | |
| {
 | |
|     pciConfigPtr pPCI;
 | |
|     int fd, result;
 | |
| 
 | |
|     pPCI = xf86GetPciHostConfigFromTag(Tag);
 | |
| 
 | |
|     if (pPCI && (result = PCI_DOM_FROM_BUS(pPCI->busnum)))
 | |
| 	return result + 1;
 | |
| 
 | |
|     if (!pPCI || pPCI->fakeDevice)
 | |
| 	return 1;		/* Domain 0 is reserved */
 | |
| 
 | |
|     if ((fd = linuxPciOpenFile(pPCI ? pPCI->tag : 0,FALSE)) < 0)
 | |
| 	return 0;
 | |
| 
 | |
|     if ((result = ioctl(fd, PCIIOC_CONTROLLER, 0)) < 0)
 | |
| 	return 0;
 | |
| 
 | |
|     return result + 1;		/* Domain 0 is reserved */
 | |
| }
 | |
| 
 | |
| static pointer
 | |
| linuxMapPci(int ScreenNum, int Flags, PCITAG Tag,
 | |
| 	    ADDRESS Base, unsigned long Size, int mmap_ioctl)
 | |
| {
 | |
|     do {
 | |
| 	pciConfigPtr pPCI;
 | |
| 	unsigned char *result;
 | |
| 	ADDRESS realBase, Offset;
 | |
| 	int fd, mmapflags, prot;
 | |
| 
 | |
| 	xf86InitVidMem();
 | |
| 
 | |
| 	if (((fd = linuxPciOpenFile(Tag ,FALSE)) < 0) ||
 | |
| 	    (ioctl(fd, mmap_ioctl, 0) < 0))
 | |
| 	    break;
 | |
| 
 | |
| /* Note:  IA-64 doesn't compile this and doesn't need to */
 | |
| #ifdef __ia64__
 | |
| 
 | |
| # ifndef  MAP_WRITECOMBINED
 | |
| #  define MAP_WRITECOMBINED 0x00010000
 | |
| # endif
 | |
| # ifndef  MAP_NONCACHED
 | |
| #  define MAP_NONCACHED     0x00020000
 | |
| # endif
 | |
| 
 | |
| 	if (Flags & VIDMEM_FRAMEBUFFER)
 | |
| 	    mmapflags = MAP_SHARED | MAP_WRITECOMBINED;
 | |
| 	else
 | |
| 	    mmapflags = MAP_SHARED | MAP_NONCACHED;
 | |
| 
 | |
| #else /* !__ia64__ */
 | |
| 
 | |
| 	mmapflags = (Flags & VIDMEM_FRAMEBUFFER) / VIDMEM_FRAMEBUFFER;
 | |
| 
 | |
| 	if (ioctl(fd, PCIIOC_WRITE_COMBINE, mmapflags) < 0)
 | |
| 	    break;
 | |
| 
 | |
| 	mmapflags = MAP_SHARED;
 | |
| 
 | |
| #endif /* ?__ia64__ */
 | |
| 
 | |
| 	/* Align to page boundary */
 | |
| 	realBase = Base & ~(getpagesize() - 1);
 | |
| 	Offset = Base - realBase;
 | |
| 
 | |
| 	if (Flags & VIDMEM_READONLY)
 | |
| 	    prot = PROT_READ;
 | |
| 	else
 | |
| 	    prot = PROT_READ | PROT_WRITE;
 | |
| 
 | |
| 	result = mmap(NULL, Size + Offset, prot, mmapflags, fd, realBase);
 | |
| 
 | |
| 	if (!result || ((pointer)result == MAP_FAILED))
 | |
| 	    return NULL;
 | |
| 
 | |
| 	xf86MakeNewMapping(ScreenNum, Flags, realBase, Size + Offset, result);
 | |
| 
 | |
| 	return result + Offset;
 | |
|     } while (0);
 | |
| 
 | |
|     if (mmap_ioctl == PCIIOC_MMAP_IS_MEM)
 | |
| 	return xf86MapVidMem(ScreenNum, Flags, Base, Size);
 | |
| 
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| #define MAX_DOMAINS 257
 | |
| static pointer DomainMmappedIO[MAX_DOMAINS];
 | |
| 
 | |
| static int
 | |
| linuxOpenLegacy(PCITAG Tag, char *name)
 | |
| {
 | |
| #define PREFIX "/sys/class/pci_bus/%04x:%02x/%s"
 | |
|     char *path;
 | |
|     int domain, bus;
 | |
|     pciBusInfo_t *pBusInfo;
 | |
|     pciConfigPtr bridge = NULL;
 | |
|     int fd;
 | |
| 
 | |
|     path = xalloc(strlen(PREFIX) + strlen(name));
 | |
|     if (!path)
 | |
| 	return -1;
 | |
| 
 | |
|     for (;;) {
 | |
| 	domain = xf86GetPciDomain(Tag);
 | |
| 	bus = PCI_BUS_NO_DOMAIN(PCI_BUS_FROM_TAG(Tag));
 | |
| 
 | |
| 	/* Domain 0 is reserved -- see xf86GetPciDomain() */
 | |
| 	if ((domain <= 0) || (domain >= MAX_DOMAINS))
 | |
| 	    FatalError("linuxOpenLegacy():  domain out of range\n");
 | |
| 
 | |
| 	sprintf(path, PREFIX, domain - 1, bus, name);
 | |
| 	fd = open(path, O_RDWR);
 | |
| 	if (fd >= 0) {
 | |
| 	    xfree(path);
 | |
| 	    return fd;
 | |
| 	}
 | |
| 
 | |
| 	pBusInfo = pciBusInfo[bus];
 | |
| 	if (!pBusInfo || (bridge == pBusInfo->bridge) ||
 | |
| 		!(bridge = pBusInfo->bridge)) {
 | |
| 	    xfree(path);
 | |
| 	    return -1;
 | |
| 	}
 | |
| 
 | |
| 	Tag = bridge->tag;
 | |
|     }
 | |
| 
 | |
|     xfree(path);
 | |
|     return fd;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * xf86MapDomainMemory - memory map PCI domain memory
 | |
|  *
 | |
|  * This routine maps the memory region in the domain specified by Tag and
 | |
|  * returns a pointer to it.  The pointer is saved for future use if it's in
 | |
|  * the legacy ISA memory space (memory in a domain between 0 and 1MB).
 | |
|  */
 | |
| _X_EXPORT pointer
 | |
| xf86MapDomainMemory(int ScreenNum, int Flags, PCITAG Tag,
 | |
| 		    ADDRESS Base, unsigned long Size)
 | |
| {
 | |
|     int domain = xf86GetPciDomain(Tag);
 | |
|     int fd = -1;
 | |
|     pointer addr;
 | |
| 
 | |
|     /*
 | |
|      * We use /proc/bus/pci on non-legacy addresses or if the Linux sysfs
 | |
|      * legacy_mem interface is unavailable.
 | |
|      */
 | |
|     if (Base >= 1024*1024)
 | |
| 	addr = linuxMapPci(ScreenNum, Flags, Tag, Base, Size,
 | |
| 			   PCIIOC_MMAP_IS_MEM);
 | |
|     else if ((fd = linuxOpenLegacy(Tag, "legacy_mem")) < 0)
 | |
| 	addr = linuxMapPci(ScreenNum, Flags, Tag, Base, Size,
 | |
| 			   PCIIOC_MMAP_IS_MEM);
 | |
|     else
 | |
| 	addr = mmap(NULL, Size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, Base);
 | |
| 
 | |
|     if (fd >= 0)
 | |
| 	close(fd);
 | |
|     if (addr == NULL || addr == MAP_FAILED) {
 | |
| 	perror("mmap failure");
 | |
| 	FatalError("xf86MapDomainMem():  mmap() failure\n");
 | |
|     }
 | |
|     return addr;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * xf86MapDomainIO - map I/O space in this domain
 | |
|  *
 | |
|  * Each domain has a legacy ISA I/O space.  This routine will try to
 | |
|  * map it using the Linux sysfs legacy_io interface.  If that fails,
 | |
|  * it'll fall back to using /proc/bus/pci.
 | |
|  *
 | |
|  * If the legacy_io interface *does* exist, the file descriptor (fd below)
 | |
|  * will be saved in the DomainMmappedIO array in the upper bits of the
 | |
|  * pointer.  Callers will do I/O with small port numbers (<64k values), so
 | |
|  * the platform I/O code can extract the port number and the fd, lseek to
 | |
|  * the port number in the legacy_io file, and issue the read or write.
 | |
|  *
 | |
|  * This has no means of returning failure, so all errors are fatal
 | |
|  */
 | |
| _X_EXPORT IOADDRESS
 | |
| xf86MapDomainIO(int ScreenNum, int Flags, PCITAG Tag,
 | |
| 		IOADDRESS Base, unsigned long Size)
 | |
| {
 | |
|     int domain = xf86GetPciDomain(Tag);
 | |
|     int fd;
 | |
| 
 | |
|     if ((domain <= 0) || (domain >= MAX_DOMAINS))
 | |
| 	FatalError("xf86MapDomainIO():  domain out of range\n");
 | |
| 
 | |
|     if (DomainMmappedIO[domain])
 | |
| 	return (IOADDRESS)DomainMmappedIO[domain] + Base;
 | |
| 
 | |
|     /* Permanently map all of I/O space */
 | |
|     if ((fd = linuxOpenLegacy(Tag, "legacy_io")) < 0) {
 | |
| 	    DomainMmappedIO[domain] = linuxMapPci(ScreenNum, Flags, Tag,
 | |
| 						  0, linuxGetIOSize(Tag),
 | |
| 						  PCIIOC_MMAP_IS_IO);
 | |
| 	    /* ia64 can't mmap legacy IO port space */
 | |
| 	    if (!DomainMmappedIO[domain])
 | |
| 		return Base;
 | |
|     }
 | |
|     else { /* legacy_io file exists, encode fd */
 | |
| 	DomainMmappedIO[domain] = (pointer)(fd << 24);
 | |
|     }
 | |
| 
 | |
|     return (IOADDRESS)DomainMmappedIO[domain] + Base;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * xf86ReadDomainMemory - copy from domain memory into a caller supplied buffer
 | |
|  */
 | |
| _X_EXPORT int
 | |
| xf86ReadDomainMemory(PCITAG Tag, ADDRESS Base, int Len, unsigned char *Buf)
 | |
| {
 | |
|     unsigned char *ptr, *src;
 | |
|     ADDRESS offset;
 | |
|     unsigned long size;
 | |
|     int len, pagemask = getpagesize() - 1;
 | |
| 
 | |
|     unsigned int i, dom, bus, dev, func;
 | |
|     unsigned int fd;
 | |
|     char file[256];
 | |
|     struct stat st;
 | |
| 
 | |
|     dom  = PCI_DOM_FROM_TAG(Tag);
 | |
|     bus  = PCI_BUS_NO_DOMAIN(PCI_BUS_FROM_TAG(Tag));
 | |
|     dev  = PCI_DEV_FROM_TAG(Tag);
 | |
|     func = PCI_FUNC_FROM_TAG(Tag);
 | |
|     sprintf(file, "/sys/devices/pci%04x:%02x/%04x:%02x:%02x.%1x/rom",
 | |
| 	    dom, bus, dom, bus, dev, func);
 | |
| 
 | |
|     /*
 | |
|      * If the caller wants the ROM and the sysfs rom interface exists,
 | |
|      * try to use it instead of reading it from /proc/bus/pci.
 | |
|      */
 | |
|     if (((Base & 0xfffff) == 0xC0000) && (stat(file, &st) == 0)) {
 | |
|         if ((fd = open(file, O_RDWR)))
 | |
|             Base = 0x0;
 | |
| 
 | |
| 	/* enable the ROM first */
 | |
| 	write(fd, "1", 2);
 | |
| 	lseek(fd, 0, SEEK_SET);
 | |
| 
 | |
|         /* copy the ROM until we hit Len, EOF or read error */
 | |
|         for (i = 0; i < Len && read(fd, Buf, 1) > 0; Buf++, i++)
 | |
|             ;
 | |
| 
 | |
| 	write(fd, "0", 2);
 | |
| 	close(fd);
 | |
| 
 | |
| 	return Len;
 | |
|     }
 | |
| 
 | |
|     /* Ensure page boundaries */
 | |
|     offset = Base & ~pagemask;
 | |
|     size = ((Base + Len + pagemask) & ~pagemask) - offset;
 | |
| 
 | |
|     ptr = xf86MapDomainMemory(-1, VIDMEM_READONLY, Tag, offset, size);
 | |
| 
 | |
|     if (!ptr)
 | |
| 	return -1;
 | |
| 
 | |
|     /* Using memcpy() here can hang the system */
 | |
|     src = ptr + (Base - offset);
 | |
|     for (len = Len;  len-- > 0;)
 | |
| 	*Buf++ = *src++;
 | |
| 
 | |
|     xf86UnMapVidMem(-1, ptr, size);
 | |
| 
 | |
|     return Len;
 | |
| }
 | |
| 
 | |
| resPtr
 | |
| xf86BusAccWindowsFromOS(void)
 | |
| {
 | |
|     pciConfigPtr  *ppPCI, pPCI;
 | |
|     resPtr        pRes = NULL;
 | |
|     resRange      range;
 | |
|     unsigned long io_size, mem_size;
 | |
|     int           domain;
 | |
| 
 | |
|     if ((ppPCI = xf86scanpci(0))) {
 | |
| 	for (;  (pPCI = *ppPCI);  ppPCI++) {
 | |
| 	    if ((pPCI->pci_base_class != PCI_CLASS_BRIDGE) ||
 | |
| 		(pPCI->pci_sub_class  != PCI_SUBCLASS_BRIDGE_HOST))
 | |
| 		continue;
 | |
| 
 | |
| 	    domain = xf86GetPciDomain(pPCI->tag);
 | |
| 	    linuxGetSizes(pPCI->tag, &io_size, &mem_size);
 | |
| 
 | |
| 	    RANGE(range, 0, (ADDRESS)(mem_size - 1),
 | |
| 		  RANGE_TYPE(ResExcMemBlock, domain));
 | |
| 	    pRes = xf86AddResToList(pRes, &range, -1);
 | |
| 
 | |
| 	    RANGE(range, 0, (IOADDRESS)(io_size - 1),
 | |
| 		  RANGE_TYPE(ResExcIoBlock, domain));
 | |
| 	    pRes = xf86AddResToList(pRes, &range, -1);
 | |
| 
 | |
| 	    if (domain <= 0)
 | |
| 		break;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     return pRes;
 | |
| }
 | |
| 
 | |
| resPtr
 | |
| xf86PciBusAccWindowsFromOS(void)
 | |
| {
 | |
|     pciConfigPtr  *ppPCI, pPCI;
 | |
|     resPtr        pRes = NULL;
 | |
|     resRange      range;
 | |
|     unsigned long io_size, mem_size;
 | |
|     int           domain;
 | |
| 
 | |
|     if ((ppPCI = xf86scanpci(0))) {
 | |
| 	for (;  (pPCI = *ppPCI);  ppPCI++) {
 | |
| 	    if ((pPCI->pci_base_class != PCI_CLASS_BRIDGE) ||
 | |
| 		(pPCI->pci_sub_class  != PCI_SUBCLASS_BRIDGE_HOST))
 | |
| 		continue;
 | |
| 
 | |
| 	    domain = xf86GetPciDomain(pPCI->tag);
 | |
| 	    linuxGetSizes(pPCI->tag, &io_size, &mem_size);
 | |
| 
 | |
| 	    RANGE(range, 0, (ADDRESS)(mem_size - 1),
 | |
| 		  RANGE_TYPE(ResExcMemBlock, domain));
 | |
| 	    pRes = xf86AddResToList(pRes, &range, -1);
 | |
| 
 | |
| 	    RANGE(range, 0, (IOADDRESS)(io_size - 1),
 | |
| 		  RANGE_TYPE(ResExcIoBlock, domain));
 | |
| 	    pRes = xf86AddResToList(pRes, &range, -1);
 | |
| 
 | |
| 	    if (domain <= 0)
 | |
| 		break;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     return pRes;
 | |
| }
 | |
| 
 | |
| 
 | |
| resPtr
 | |
| xf86AccResFromOS(resPtr pRes)
 | |
| {
 | |
|     pciConfigPtr  *ppPCI, pPCI;
 | |
|     resRange      range;
 | |
|     unsigned long io_size, mem_size;
 | |
|     int           domain;
 | |
| 
 | |
|     if ((ppPCI = xf86scanpci(0))) {
 | |
| 	for (;  (pPCI = *ppPCI);  ppPCI++) {
 | |
| 	    if ((pPCI->pci_base_class != PCI_CLASS_BRIDGE) ||
 | |
| 		(pPCI->pci_sub_class  != PCI_SUBCLASS_BRIDGE_HOST))
 | |
| 		continue;
 | |
| 
 | |
| 	    domain = xf86GetPciDomain(pPCI->tag);
 | |
| 	    linuxGetSizes(pPCI->tag, &io_size, &mem_size);
 | |
| 
 | |
| 	    /*
 | |
| 	     * At minimum, the top and bottom resources must be claimed, so
 | |
| 	     * that resources that are (or appear to be) unallocated can be
 | |
| 	     * relocated.
 | |
| 	     */
 | |
| 	    RANGE(range, 0x00000000u, 0x0009ffffu,
 | |
| 		  RANGE_TYPE(ResExcMemBlock, domain));
 | |
| 	    pRes = xf86AddResToList(pRes, &range, -1);
 | |
| 	    RANGE(range, 0x000c0000u, 0x000effffu,
 | |
| 		  RANGE_TYPE(ResExcMemBlock, domain));
 | |
| 	    pRes = xf86AddResToList(pRes, &range, -1);
 | |
| 	    RANGE(range, 0x000f0000u, 0x000fffffu,
 | |
| 		  RANGE_TYPE(ResExcMemBlock, domain));
 | |
| 	    pRes = xf86AddResToList(pRes, &range, -1);
 | |
| 
 | |
| 	    RANGE(range, (ADDRESS)(mem_size - 1), (ADDRESS)(mem_size - 1),
 | |
| 		  RANGE_TYPE(ResExcMemBlock, domain));
 | |
| 	    pRes = xf86AddResToList(pRes, &range, -1);
 | |
| 
 | |
| 	    RANGE(range, 0x00000000u, 0x00000000u,
 | |
| 		  RANGE_TYPE(ResExcIoBlock, domain));
 | |
| 	    pRes = xf86AddResToList(pRes, &range, -1);
 | |
| 	    RANGE(range, (IOADDRESS)(io_size - 1), (IOADDRESS)(io_size - 1),
 | |
| 		  RANGE_TYPE(ResExcIoBlock, domain));
 | |
| 	    pRes = xf86AddResToList(pRes, &range, -1);
 | |
| 
 | |
| 	    if (domain <= 0)
 | |
| 		break;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     return pRes;
 | |
| }
 | |
| 
 | |
| #endif /* !INCLUDE_XF86_NO_DOMAIN */
 | |
| 
 | |
| int linuxPciHandleBIOS(PCITAG Tag, int basereg, unsigned char *buf, int len)
 | |
| {
 | |
|   unsigned int dom, bus, dev, func;
 | |
|   unsigned int fd;
 | |
|   char file[256];
 | |
|   struct stat st;
 | |
|   int ret;
 | |
|   int sofar = 0;
 | |
| 
 | |
|   dom  = PCI_DOM_FROM_TAG(Tag);
 | |
|   bus  = PCI_BUS_NO_DOMAIN(PCI_BUS_FROM_TAG(Tag));
 | |
|   dev  = PCI_DEV_FROM_TAG(Tag);
 | |
|   func = PCI_FUNC_FROM_TAG(Tag);
 | |
|   sprintf(file, "/sys/bus/pci/devices/%04x:%02x:%02x.%1x/rom",
 | |
| 	  dom, bus, dev, func);
 | |
| 
 | |
|   if (stat(file, &st) == 0)
 | |
|   {
 | |
|     if ((fd = open(file, O_RDWR)))
 | |
|       basereg = 0x0;
 | |
|     
 | |
|     /* enable the ROM first */
 | |
|     write(fd, "1", 2);
 | |
|     lseek(fd, 0, SEEK_SET);
 | |
|     do {
 | |
|         /* copy the ROM until we hit Len, EOF or read error */
 | |
|     	ret = read(fd, buf+sofar, len-sofar);
 | |
|     	if (ret <= 0)
 | |
| 		break;
 | |
| 	sofar += ret;
 | |
|     } while (sofar < len);
 | |
|     
 | |
|     write(fd, "0", 2);
 | |
|     close(fd);
 | |
|     if (sofar < len)
 | |
|     	xf86MsgVerb(X_INFO, 3, "Attempted to read BIOS %dKB from %s: got %dKB\n", len/1024, file, sofar/1024);
 | |
|     return sofar;
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| #ifdef __ia64__
 | |
| static PCITAG ia64linuxPciFindFirst(void);
 | |
| static PCITAG ia64linuxPciFindNext(void);
 | |
| 
 | |
| void   
 | |
| ia64linuxPciInit()
 | |
| {
 | |
|     struct stat st;
 | |
| 
 | |
|     linuxPciInit();
 | |
| 	   
 | |
|     if (!stat("/proc/sgi_sn/licenseID", &st) && pciNumBuses) {
 | |
|        /* Be a little paranoid here and only use this code for Altix systems.
 | |
| 	* It is generic, so it should work on any system, but depends on
 | |
| 	* /proc/bus/pci entries for each domain/bus combination. Altix is
 | |
| 	* guaranteed a recent enough kernel to have them.
 | |
| 	*/
 | |
|        pciFindFirstFP = ia64linuxPciFindFirst;
 | |
|        pciFindNextFP  = ia64linuxPciFindNext;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static DIR *busdomdir;
 | |
| static DIR *devdir;
 | |
| 	       
 | |
| static PCITAG
 | |
| ia64linuxPciFindFirst(void)
 | |
| {   
 | |
|        busdomdir = opendir("/proc/bus/pci");
 | |
|        devdir = NULL;
 | |
| 
 | |
|        return ia64linuxPciFindNext();
 | |
| }   
 | |
| 
 | |
| static struct dirent *getnextbus(int *domain, int *bus)
 | |
| {
 | |
|     struct dirent *entry;
 | |
|     int dombus;
 | |
| 
 | |
|     for (;;) {
 | |
| 	entry = readdir(busdomdir);
 | |
| 	if (entry == NULL) {
 | |
| 	    *domain = 0;
 | |
| 	    *bus = 0;
 | |
| 	    closedir(busdomdir);
 | |
| 	    return NULL;
 | |
| 	}
 | |
| 	if (sscanf(entry->d_name, "%04x:%02x", domain, bus) != 2)
 | |
| 	    continue;
 | |
| 	dombus = PCI_MAKE_BUS(*domain, *bus);
 | |
| 
 | |
| 	if (pciNumBuses <= dombus)
 | |
| 	    pciNumBuses = dombus + 1;
 | |
| 	if (!pciBusInfo[dombus]) {
 | |
| 	    pciBusInfo[dombus] = xnfalloc(sizeof(pciBusInfo_t));
 | |
| 	    *pciBusInfo[dombus] = *pciBusInfo[0];
 | |
| 	}
 | |
| 
 | |
| 	return entry;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static PCITAG
 | |
| ia64linuxPciFindNext(void)
 | |
| {
 | |
|     struct dirent *entry;
 | |
|     char file[40];
 | |
|     static int bus, dev, func, domain;
 | |
|     PCITAG pciDeviceTag;
 | |
|     CARD32 devid;
 | |
| 
 | |
|     for (;;) {
 | |
| 	if (devdir == NULL) {
 | |
| 	    entry = getnextbus(&domain, &bus);
 | |
| 	    if (!entry)
 | |
| 		return PCI_NOT_FOUND;
 | |
| 	    snprintf(file, 40, "/proc/bus/pci/%s", entry->d_name);
 | |
| 	    devdir = opendir(file);
 | |
| 	    if (!devdir)
 | |
| 		return PCI_NOT_FOUND;
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	entry = readdir(devdir);
 | |
| 
 | |
| 	if (entry == NULL) {
 | |
| 	    closedir(devdir);
 | |
| 	    devdir = NULL;
 | |
| 	    continue;
 | |
| 	}
 | |
| 
 | |
| 	if (sscanf(entry->d_name, "%02x . %01x", &dev, &func) == 2) {
 | |
| 	    pciDeviceTag = PCI_MAKE_TAG(PCI_MAKE_BUS(domain, bus), dev, func);
 | |
| 	    devid = pciReadLong(pciDeviceTag, PCI_ID_REG);
 | |
| 	    if ((devid & pciDevidMask) == pciDevid)
 | |
| 		/* Yes - Return it.  Otherwise, next device */
 | |
| 		return pciDeviceTag;
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| #endif
 | |
| 
 |