/* * 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 #endif #include #include #include #include #include #include "os.h" #include "Pci.h" #include "xf86.h" #include "xf86Priv.h" #include "dirent.h" /* DIR, FILE type definitions */ /* Bus-specific headers */ #include "xf86Bus.h" #define XF86_OS_PRIVS #include "xf86_OSproc.h" #define PCI_VENDOR_GENERIC 0x00FF /* Bus-specific globals */ int pciSlotClaimed = 0; #define PCIINFOCLASSES(c) \ ( (((c) & 0x00ff0000) == (PCI_CLASS_PREHISTORIC << 16)) \ || (((c) & 0x00ff0000) == (PCI_CLASS_DISPLAY << 16)) \ || ((((c) & 0x00ffff00) \ == ((PCI_CLASS_MULTIMEDIA << 16) | (PCI_SUBCLASS_MULTIMEDIA_VIDEO << 8)))) \ || ((((c) & 0x00ffff00) \ == ((PCI_CLASS_PROCESSOR << 16) | (PCI_SUBCLASS_PROCESSOR_COPROC << 8)))) ) /* * PCI classes that have messages printed always. The others are only * have a message printed when the vendor/dev IDs are recognised. */ #define PCIALWAYSPRINTCLASSES(c) \ ( (((c) & 0x00ffff00) \ == ((PCI_CLASS_PREHISTORIC << 16) | (PCI_SUBCLASS_PREHISTORIC_VGA << 8))) \ || (((c) & 0x00ff0000) == (PCI_CLASS_DISPLAY << 16)) \ || ((((c) & 0x00ffff00) \ == ((PCI_CLASS_MULTIMEDIA << 16) | (PCI_SUBCLASS_MULTIMEDIA_VIDEO << 8)))) ) #define IS_VGA(c) \ (((c) & 0x00ffff00) \ == ((PCI_CLASS_DISPLAY << 16) | (PCI_SUBCLASS_DISPLAY_VGA << 8))) static struct pci_slot_match xf86IsolateDevice = { PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, 0 }; /* * xf86Bus.c interface */ void xf86PciProbe(void) { int i = 0, k; int num = 0; struct pci_device *info; struct pci_device_iterator *iter; struct pci_device **xf86PciVideoInfo = NULL; if (!xf86scanpci()) { xf86PciVideoInfo = NULL; return; } iter = pci_slot_match_iterator_create(&xf86IsolateDevice); while ((info = pci_device_next(iter)) != NULL) { if (PCIINFOCLASSES(info->device_class)) { num++; xf86PciVideoInfo = xnfreallocarray(xf86PciVideoInfo, num + 1, sizeof(struct pci_device *)); xf86PciVideoInfo[num] = NULL; xf86PciVideoInfo[num - 1] = info; pci_device_probe(info); if (primaryBus.type == BUS_NONE && pci_device_is_boot_vga(info)) { primaryBus.type = BUS_PCI; primaryBus.id.pci = info; } info->user_data = 0; } } free(iter); /* If we haven't found a primary device try a different heuristic */ if (primaryBus.type == BUS_NONE && num) { for (i = 0; i < num; i++) { uint16_t command; info = xf86PciVideoInfo[i]; pci_device_cfg_read_u16(info, &command, 4); if ((command & PCI_CMD_MEM_ENABLE) && ((num == 1) || IS_VGA(info->device_class))) { if (primaryBus.type == BUS_NONE) { primaryBus.type = BUS_PCI; primaryBus.id.pci = info; } else { xf86Msg(X_NOTICE, "More than one possible primary device found\n"); primaryBus.type ^= (BusType) (-1); } } } } /* Print a summary of the video devices found */ for (k = 0; k < num; k++) { const char *prim = " "; Bool memdone = FALSE, iodone = FALSE; info = xf86PciVideoInfo[k]; if (!PCIALWAYSPRINTCLASSES(info->device_class)) continue; if (xf86IsPrimaryPci(info)) prim = "*"; xf86Msg(X_PROBED, "PCI:%s(%u@%u:%u:%u) %04x:%04x:%04x:%04x ", prim, info->bus, info->domain, info->dev, info->func, info->vendor_id, info->device_id, info->subvendor_id, info->subdevice_id); xf86ErrorF("rev %d", info->revision); for (i = 0; i < 6; i++) { struct pci_mem_region *r = &info->regions[i]; if (r->size && !r->is_IO) { if (!memdone) { xf86ErrorF(", Mem @ "); memdone = TRUE; } else xf86ErrorF(", "); xf86ErrorF("0x%08lx/%ld", (long) r->base_addr, (long) r->size); } } for (i = 0; i < 6; i++) { struct pci_mem_region *r = &info->regions[i]; if (r->size && r->is_IO) { if (!iodone) { xf86ErrorF(", I/O @ "); iodone = TRUE; } else xf86ErrorF(", "); xf86ErrorF("0x%08lx/%ld", (long) r->base_addr, (long) r->size); } } if (info->rom_size) { xf86ErrorF(", BIOS @ 0x\?\?\?\?\?\?\?\?/%ld", (long) info->rom_size); } xf86ErrorF("\n"); } free(xf86PciVideoInfo); } /* * If the slot requested is already in use, return -1. * Otherwise, claim the slot for the screen requesting it. */ int xf86ClaimPciSlot(struct pci_device *d, DriverPtr drvp, int chipset, GDevPtr dev, Bool active) { EntityPtr p = NULL; int num; if (xf86CheckPciSlot(d)) { num = xf86AllocateEntity(); p = xf86Entities[num]; p->driver = drvp; p->chipset = chipset; p->bus.type = BUS_PCI; p->bus.id.pci = d; p->active = active; p->inUse = FALSE; if (dev) xf86AddDevToEntity(num, dev); pciSlotClaimed++; return num; } else return -1; } /* * Unclaim PCI slot, e.g. if probing failed, so that a different driver can claim. */ void xf86UnclaimPciSlot(struct pci_device *d, GDevPtr dev) { int i; for (i = 0; i < xf86NumEntities; i++) { const EntityPtr p = xf86Entities[i]; if ((p->bus.type == BUS_PCI) && (p->bus.id.pci == d)) { /* Probably the slot should be deallocated? */ xf86RemoveDevFromEntity(i, dev); pciSlotClaimed--; p->bus.type = BUS_NONE; return; } } } /* * Parse a BUS ID string, and return the PCI bus parameters if it was * in the correct format for a PCI bus id. */ Bool xf86ParsePciBusString(const char *busID, int *bus, int *device, int *func) { /* * The format is assumed to be "bus[@domain]:device[:func]", where domain, * bus, device and func are decimal integers. domain and func may be * omitted and assumed to be zero, although doing this isn't encouraged. */ char *p, *s, *d; const char *id; int i; if (StringToBusType(busID, &id) != BUS_PCI) return FALSE; s = xstrdup(id); p = strtok(s, ":"); if (p == NULL || *p == 0) { free(s); return FALSE; } d = strpbrk(p, "@"); if (d != NULL) { *(d++) = 0; for (i = 0; d[i] != 0; i++) { if (!isdigit(d[i])) { free(s); return FALSE; } } } for (i = 0; p[i] != 0; i++) { if (!isdigit(p[i])) { free(s); return FALSE; } } *bus = atoi(p); if (d != NULL && *d != 0) *bus += atoi(d) << 8; p = strtok(NULL, ":"); if (p == NULL || *p == 0) { free(s); return FALSE; } for (i = 0; p[i] != 0; i++) { if (!isdigit(p[i])) { free(s); return FALSE; } } *device = atoi(p); *func = 0; p = strtok(NULL, ":"); if (p == NULL || *p == 0) { free(s); return TRUE; } for (i = 0; p[i] != 0; i++) { if (!isdigit(p[i])) { free(s); return FALSE; } } *func = atoi(p); free(s); return TRUE; } /* * Compare a BUS ID string with a PCI bus id. Return TRUE if they match. */ Bool xf86ComparePciBusString(const char *busID, int bus, int device, int func) { int ibus, idevice, ifunc; if (xf86ParsePciBusString(busID, &ibus, &idevice, &ifunc)) { return bus == ibus && device == idevice && func == ifunc; } else { return FALSE; } } /* * xf86IsPrimaryPci() -- return TRUE if primary device * is PCI and bus, dev and func numbers match. */ Bool xf86IsPrimaryPci(struct pci_device *pPci) { /* Add max. 1 screen for the IgnorePrimary fallback path */ if (xf86ProbeIgnorePrimary && xf86NumScreens == 0) return TRUE; if (primaryBus.type == BUS_PCI) return pPci == primaryBus.id.pci; #ifdef XSERVER_PLATFORM_BUS if (primaryBus.type == BUS_PLATFORM) if (primaryBus.id.plat->pdev) if (MATCH_PCI_DEVICES(primaryBus.id.plat->pdev, pPci)) return TRUE; #endif return FALSE; } /* * xf86GetPciInfoForEntity() -- Get the pciVideoRec of entity. */ struct pci_device * xf86GetPciInfoForEntity(int entityIndex) { EntityPtr p; if (entityIndex >= xf86NumEntities) return NULL; p = xf86Entities[entityIndex]; switch (p->bus.type) { case BUS_PCI: return p->bus.id.pci; case BUS_PLATFORM: return p->bus.id.plat->pdev; default: break; } return NULL; } /* * xf86CheckPciMemBase() checks that the memory base value matches one of the * PCI base address register values for the given PCI device. */ Bool xf86CheckPciMemBase(struct pci_device *pPci, memType base) { int i; for (i = 0; i < 6; i++) if (base == pPci->regions[i].base_addr) return TRUE; return FALSE; } /* * Check if the slot requested is free. If it is already in use, return FALSE. */ Bool xf86CheckPciSlot(const struct pci_device *d) { int i; for (i = 0; i < xf86NumEntities; i++) { const EntityPtr p = xf86Entities[i]; if ((p->bus.type == BUS_PCI) && (p->bus.id.pci == d)) { return FALSE; } #ifdef XSERVER_PLATFORM_BUS if ((p->bus.type == BUS_PLATFORM) && (p->bus.id.plat->pdev)) { struct pci_device *ud = p->bus.id.plat->pdev; if (MATCH_PCI_DEVICES(ud, d)) return FALSE; } #endif } return TRUE; } #define END_OF_MATCHES(m) \ (((m).vendor_id == 0) && ((m).device_id == 0) && ((m).subvendor_id == 0)) Bool xf86PciAddMatchingDev(DriverPtr drvp) { const struct pci_id_match *const devices = drvp->supported_devices; int j; struct pci_device *pPci; struct pci_device_iterator *iter; int numFound = 0; iter = pci_id_match_iterator_create(NULL); while ((pPci = pci_device_next(iter)) != NULL) { /* Determine if this device is supported by the driver. If it is, * add it to the list of devices to configure. */ for (j = 0; !END_OF_MATCHES(devices[j]); j++) { if (PCI_ID_COMPARE(devices[j].vendor_id, pPci->vendor_id) && PCI_ID_COMPARE(devices[j].device_id, pPci->device_id) && ((devices[j].device_class_mask & pPci->device_class) == devices[j].device_class)) { if (xf86CheckPciSlot(pPci)) { GDevPtr pGDev = xf86AddBusDeviceToConfigure(drvp->driverName, BUS_PCI, pPci, -1); if (pGDev != NULL) { /* After configure pass 1, chipID and chipRev are * treated as over-rides, so clobber them here. */ pGDev->chipID = -1; pGDev->chipRev = -1; } numFound++; } break; } } } pci_iterator_destroy(iter); return numFound != 0; } Bool xf86PciProbeDev(DriverPtr drvp) { int i, j; struct pci_device *pPci; Bool foundScreen = FALSE; const struct pci_id_match *const devices = drvp->supported_devices; GDevPtr *devList; const unsigned numDevs = xf86MatchDevice(drvp->driverName, &devList); for (i = 0; i < numDevs; i++) { struct pci_device_iterator *iter; unsigned device_id; /* Find the pciVideoRec associated with this device section. */ iter = pci_id_match_iterator_create(NULL); while ((pPci = pci_device_next(iter)) != NULL) { if (devList[i]->busID && *devList[i]->busID) { if (xf86ComparePciBusString(devList[i]->busID, ((pPci->domain << 8) | pPci->bus), pPci->dev, pPci->func)) { break; } } else if (xf86IsPrimaryPci(pPci)) { break; } } pci_iterator_destroy(iter); if (pPci == NULL) { continue; } device_id = (devList[i]->chipID > 0) ? devList[i]->chipID : pPci->device_id; /* Once the pciVideoRec is found, determine if the device is supported * by the driver. If it is, probe it! */ for (j = 0; !END_OF_MATCHES(devices[j]); j++) { if (PCI_ID_COMPARE(devices[j].vendor_id, pPci->vendor_id) && PCI_ID_COMPARE(devices[j].device_id, device_id) && ((devices[j].device_class_mask & pPci->device_class) == devices[j].device_class)) { int entry; /* Allow the same entity to be used more than once for * devices with multiple screens per entity. This assumes * implicitly that there will be a screen == 0 instance. * * FIXME Need to make sure that two different drivers don't * FIXME claim the same screen > 0 instance. */ if ((devList[i]->screen == 0) && !xf86CheckPciSlot(pPci)) continue; DebugF("%s: card at %d:%d:%d is claimed by a Device section\n", drvp->driverName, pPci->bus, pPci->dev, pPci->func); /* Allocate an entry in the lists to be returned */ entry = xf86ClaimPciSlot(pPci, drvp, device_id, devList[i], devList[i]->active); if ((entry == -1) && (devList[i]->screen > 0)) { unsigned k; for (k = 0; k < xf86NumEntities; k++) { EntityPtr pEnt = xf86Entities[k]; if (pEnt->bus.type != BUS_PCI) continue; if (pEnt->bus.id.pci == pPci) { entry = k; xf86AddDevToEntity(k, devList[i]); break; } } } if (entry != -1) { if ((*drvp->PciProbe) (drvp, entry, pPci, devices[j].match_data)) { foundScreen = TRUE; } else xf86UnclaimPciSlot(pPci, devList[i]); } break; } } } free(devList); return foundScreen; } void xf86PciIsolateDevice(const char *argument) { int bus, device, func; if (sscanf(argument, "PCI:%d:%d:%d", &bus, &device, &func) == 3) { xf86IsolateDevice.domain = PCI_DOM_FROM_BUS(bus); xf86IsolateDevice.bus = PCI_BUS_NO_DOMAIN(bus); xf86IsolateDevice.dev = device; xf86IsolateDevice.func = func; } else FatalError("Invalid isolated device specification\n"); } static Bool pciDeviceHasBars(struct pci_device *pci) { int i; for (i = 0; i < 6; i++) if (pci->regions[i].size) return TRUE; if (pci->rom_size) return TRUE; return FALSE; } struct Inst { struct pci_device *pci; GDevPtr dev; Bool foundHW; /* PCIid in list of supported chipsets */ Bool claimed; /* BusID matches with a device section */ int chip; int screen; }; /** * Find set of unclaimed devices matching a given vendor ID. * * Used by drivers to find as yet unclaimed devices matching the specified * vendor ID. * * \param driverName Name of the driver. This is used to find Device * sections in the config file. * \param vendorID PCI vendor ID of associated devices. If zero, then * the true vendor ID must be encoded in the \c PCIid * fields of the \c PCIchipsets entries. * \param chipsets Symbol table used to associate chipset names with * PCI IDs. * \param devList List of Device sections parsed from the config file. * \param numDevs Number of entries in \c devList. * \param drvp Pointer the driver's control structure. * \param foundEntities Returned list of entity indicies associated with the * driver. * * \returns * The number of elements in returned in \c foundEntities on success or zero * on failure. * * \todo * This function does a bit more than short description says. Fill in some * more of the details of its operation. * * \todo * The \c driverName parameter is redundant. It is the same as * \c DriverRec::driverName. In a future version of this function, remove * that parameter. */ int xf86MatchPciInstances(const char *driverName, int vendorID, SymTabPtr chipsets, PciChipsets * PCIchipsets, GDevPtr * devList, int numDevs, DriverPtr drvp, int **foundEntities) { int i, j; struct pci_device *pPci; struct pci_device_iterator *iter; struct Inst *instances = NULL; int numClaimedInstances = 0; int allocatedInstances = 0; int numFound = 0; SymTabRec *c; PciChipsets *id; int *retEntities = NULL; *foundEntities = NULL; /* Each PCI device will contribute at least one entry. Each device * section can contribute at most one entry. The sum of the two is * guaranteed to be larger than the maximum possible number of entries. * Do this calculation and memory allocation once now to eliminate the * need for realloc calls inside the loop. */ if (!(xf86DoConfigure && xf86DoConfigurePass1)) { unsigned max_entries = numDevs; iter = pci_slot_match_iterator_create(NULL); while ((pPci = pci_device_next(iter)) != NULL) { max_entries++; } pci_iterator_destroy(iter); instances = xnfallocarray(max_entries, sizeof(struct Inst)); } iter = pci_slot_match_iterator_create(NULL); while ((pPci = pci_device_next(iter)) != NULL) { unsigned device_class = pPci->device_class; Bool foundVendor = FALSE; /* Convert the pre-PCI 2.0 device class for a VGA adapter to the * 2.0 version of the same class. */ if (device_class == 0x00000101) { device_class = 0x00030000; } /* Find PCI devices that match the given vendor ID. The vendor ID is * either specified explicitly as a parameter to the function or * implicitly encoded in the high bits of id->PCIid. * * The first device with a matching vendor is recorded, even if the * device ID doesn't match. This is done because the Device section * in the xorg.conf file can over-ride the device ID. A matching PCI * ID might not be found now, but after the device ID over-ride is * applied there /might/ be a match. */ for (id = PCIchipsets; id->PCIid != -1; id++) { const unsigned vendor_id = ((id->PCIid & 0xFFFF0000) >> 16) | vendorID; const unsigned device_id = (id->PCIid & 0x0000FFFF); const unsigned match_class = 0x00030000 | id->PCIid; if ((vendor_id == pPci->vendor_id) || ((vendorID == PCI_VENDOR_GENERIC) && (match_class == device_class))) { if (!foundVendor && (instances != NULL)) { ++allocatedInstances; instances[allocatedInstances - 1].pci = pPci; instances[allocatedInstances - 1].dev = NULL; instances[allocatedInstances - 1].claimed = FALSE; instances[allocatedInstances - 1].foundHW = FALSE; instances[allocatedInstances - 1].screen = 0; } foundVendor = TRUE; if ((device_id == pPci->device_id) || ((vendorID == PCI_VENDOR_GENERIC) && (match_class == device_class))) { if (instances != NULL) { instances[allocatedInstances - 1].foundHW = TRUE; instances[allocatedInstances - 1].chip = id->numChipset; } if (xf86DoConfigure && xf86DoConfigurePass1) { if (xf86CheckPciSlot(pPci)) { GDevPtr pGDev = xf86AddBusDeviceToConfigure(drvp->driverName, BUS_PCI, pPci, -1); if (pGDev) { /* After configure pass 1, chipID and chipRev * are treated as over-rides, so clobber them * here. */ pGDev->chipID = -1; pGDev->chipRev = -1; } numFound++; } } else { numFound++; } break; } } } } pci_iterator_destroy(iter); /* In "probe only" or "configure" mode (signaled by instances being NULL), * our work is done. Return the number of detected devices. */ if (instances == NULL) { return numFound; } /* * This may be debatable, but if no PCI devices with a matching vendor * type is found, return zero now. It is probably not desirable to * allow the config file to override this. */ if (allocatedInstances <= 0) { free(instances); return 0; } DebugF("%s instances found: %d\n", driverName, allocatedInstances); /* * Check for devices that need duplicated instances. This is required * when there is more than one screen per entity. * * XXX This currently doesn't work for cases where the BusID isn't * specified explicitly in the config file. */ for (j = 0; j < numDevs; j++) { if (devList[j]->screen > 0 && devList[j]->busID && *devList[j]->busID) { for (i = 0; i < allocatedInstances; i++) { pPci = instances[i].pci; if (xf86ComparePciBusString(devList[j]->busID, PCI_MAKE_BUS(pPci->domain, pPci->bus), pPci->dev, pPci->func)) { allocatedInstances++; instances[allocatedInstances - 1] = instances[i]; instances[allocatedInstances - 1].screen = devList[j]->screen; numFound++; break; } } } } for (i = 0; i < allocatedInstances; i++) { GDevPtr dev = NULL; GDevPtr devBus = NULL; pPci = instances[i].pci; for (j = 0; j < numDevs; j++) { if (devList[j]->busID && *devList[j]->busID) { if (xf86ComparePciBusString(devList[j]->busID, PCI_MAKE_BUS(pPci->domain, pPci->bus), pPci->dev, pPci->func) && devList[j]->screen == instances[i].screen) { if (devBus) xf86MsgVerb(X_WARNING, 0, "%s: More than one matching Device section for " "instances\n\t(BusID: %s) found: %s\n", driverName, devList[j]->busID, devList[j]->identifier); else devBus = devList[j]; } } else { /* * if device section without BusID is found * only assign to it to the primary device. */ if (xf86IsPrimaryPci(pPci)) { xf86Msg(X_PROBED, "Assigning device section with no busID" " to primary device\n"); if (dev || devBus) xf86MsgVerb(X_WARNING, 0, "%s: More than one matching Device section " "found: %s\n", driverName, devList[j]->identifier); else dev = devList[j]; } } } if (devBus) dev = devBus; /* busID preferred */ if (!dev) { if (xf86CheckPciSlot(pPci) && pciDeviceHasBars(pPci)) { xf86MsgVerb(X_WARNING, 0, "%s: No matching Device section " "for instance (BusID PCI:%u@%u:%u:%u) found\n", driverName, pPci->bus, pPci->domain, pPci->dev, pPci->func); } } else { numClaimedInstances++; instances[i].claimed = TRUE; instances[i].dev = dev; } } DebugF("%s instances found: %d\n", driverName, numClaimedInstances); /* * Now check that a chipset or chipID override in the device section * is valid. Chipset has precedence over chipID. * If chipset is not valid ignore BusSlot completely. */ for (i = 0; i < allocatedInstances && numClaimedInstances > 0; i++) { MessageType from = X_PROBED; if (!instances[i].claimed) { continue; } if (instances[i].dev->chipset) { for (c = chipsets; c->token >= 0; c++) { if (xf86NameCmp(c->name, instances[i].dev->chipset) == 0) break; } if (c->token == -1) { instances[i].claimed = FALSE; numClaimedInstances--; xf86MsgVerb(X_WARNING, 0, "%s: Chipset \"%s\" in Device " "section \"%s\" isn't valid for this driver\n", driverName, instances[i].dev->chipset, instances[i].dev->identifier); } else { instances[i].chip = c->token; for (id = PCIchipsets; id->numChipset >= 0; id++) { if (id->numChipset == instances[i].chip) break; } if (id->numChipset >= 0) { xf86Msg(X_CONFIG, "Chipset override: %s\n", instances[i].dev->chipset); from = X_CONFIG; } else { instances[i].claimed = FALSE; numClaimedInstances--; xf86MsgVerb(X_WARNING, 0, "%s: Chipset \"%s\" in Device " "section \"%s\" isn't a valid PCI chipset\n", driverName, instances[i].dev->chipset, instances[i].dev->identifier); } } } else if (instances[i].dev->chipID > 0) { for (id = PCIchipsets; id->numChipset >= 0; id++) { if (id->PCIid == instances[i].dev->chipID) break; } if (id->numChipset == -1) { instances[i].claimed = FALSE; numClaimedInstances--; xf86MsgVerb(X_WARNING, 0, "%s: ChipID 0x%04X in Device " "section \"%s\" isn't valid for this driver\n", driverName, instances[i].dev->chipID, instances[i].dev->identifier); } else { instances[i].chip = id->numChipset; xf86Msg(X_CONFIG, "ChipID override: 0x%04X\n", instances[i].dev->chipID); from = X_CONFIG; } } else if (!instances[i].foundHW) { /* * This means that there was no override and the PCI chipType * doesn't match one that is supported */ instances[i].claimed = FALSE; numClaimedInstances--; } if (instances[i].claimed == TRUE) { for (c = chipsets; c->token >= 0; c++) { if (c->token == instances[i].chip) break; } xf86Msg(from, "Chipset %s found\n", c->name); } } /* * Of the claimed instances, check that another driver hasn't already * claimed its slot. */ numFound = 0; for (i = 0; i < allocatedInstances && numClaimedInstances > 0; i++) { if (!instances[i].claimed) continue; pPci = instances[i].pci; /* * Allow the same entity to be used more than once for devices with * multiple screens per entity. This assumes implicitly that there * will be a screen == 0 instance. * * XXX Need to make sure that two different drivers don't claim * the same screen > 0 instance. */ if (instances[i].screen == 0 && !xf86CheckPciSlot(pPci)) continue; DebugF("%s: card at %d:%d:%d is claimed by a Device section\n", driverName, pPci->bus, pPci->dev, pPci->func); /* Allocate an entry in the lists to be returned */ numFound++; retEntities = xnfreallocarray(retEntities, numFound, sizeof(int)); retEntities[numFound - 1] = xf86ClaimPciSlot(pPci, drvp, instances[i].chip, instances[i].dev, instances[i].dev->active); if (retEntities[numFound - 1] == -1 && instances[i].screen > 0) { for (j = 0; j < xf86NumEntities; j++) { EntityPtr pEnt = xf86Entities[j]; if (pEnt->bus.type != BUS_PCI) continue; if (pEnt->bus.id.pci == pPci) { retEntities[numFound - 1] = j; xf86AddDevToEntity(j, instances[i].dev); break; } } } } free(instances); if (numFound > 0) { *foundEntities = retEntities; } return numFound; } /* * xf86ConfigPciEntityInactive() -- This function can be used * to configure an inactive entity as well as to reconfigure an * previously active entity inactive. If the entity has been * assigned to a screen before it will be removed. If p_chip is * non-NULL all static resources listed there will be registered. */ static void xf86ConfigPciEntityInactive(EntityInfoPtr pEnt, PciChipsets * p_chip, EntityProc init, EntityProc enter, EntityProc leave, void *private) { ScrnInfoPtr pScrn; if ((pScrn = xf86FindScreenForEntity(pEnt->index))) xf86RemoveEntityFromScreen(pScrn, pEnt->index); } ScrnInfoPtr xf86ConfigPciEntity(ScrnInfoPtr pScrn, int scrnFlag, int entityIndex, PciChipsets * p_chip, void *dummy, EntityProc init, EntityProc enter, EntityProc leave, void *private) { EntityInfoPtr pEnt = xf86GetEntityInfo(entityIndex); if (dummy || init || enter || leave) FatalError("Legacy entity access functions are unsupported\n"); if (!pEnt) return pScrn; if (!(pEnt->location.type == BUS_PCI) || !xf86GetPciInfoForEntity(entityIndex)) { free(pEnt); return pScrn; } if (!pEnt->active) { xf86ConfigPciEntityInactive(pEnt, p_chip, init, enter, leave, private); free(pEnt); return pScrn; } if (!pScrn) pScrn = xf86AllocateScreen(pEnt->driver, scrnFlag); if (xf86IsEntitySharable(entityIndex)) { xf86SetEntityShared(entityIndex); } xf86AddEntityToScreen(pScrn, entityIndex); if (xf86IsEntityShared(entityIndex)) { return pScrn; } free(pEnt); return pScrn; } void xf86VideoPtrToDriverList(struct pci_device *dev, XF86MatchedDrivers *md) { int i; /* Add more entries here if we ever return more than 4 drivers for any device */ const char *driverList[5] = { NULL, NULL, NULL, NULL, NULL }; switch (dev->vendor_id) { /* AMD Geode LX */ case 0x1022: if (dev->device_id == 0x2081) driverList[0] = "geode"; break; /* older Geode products acquired by AMD still carry an NSC vendor_id */ case 0x100b: if (dev->device_id == 0x0030) { /* NSC Geode GX2 specifically */ driverList[0] = "geode"; /* GX2 support started its life in the NSC tree and was later forked by AMD for GEODE so we keep it as a backup */ driverList[1] = "nsc"; } else /* other NSC variant e.g. 0x0104 (SC1400), 0x0504 (SCx200) */ driverList[0] = "nsc"; break; /* Cyrix Geode GX1 */ case 0x1078: if (dev->device_id == 0x0104) driverList[0] = "cyrix"; break; case 0x1142: driverList[0] = "apm"; break; case 0xedd8: driverList[0] = "ark"; break; case 0x1a03: driverList[0] = "ast"; break; case 0x1002: driverList[0] = "ati"; break; case 0x102c: driverList[0] = "chips"; break; case 0x1013: driverList[0] = "cirrus"; break; case 0x3d3d: driverList[0] = "glint"; break; case 0x105d: driverList[0] = "i128"; break; case 0x8086: switch (dev->device_id) { /* Intel i740 */ case 0x00d1: case 0x7800: driverList[0] = "i740"; break; /* GMA500/Poulsbo */ case 0x8108: case 0x8109: /* Try psb driver on Poulsbo - if available */ driverList[0] = "psb"; driverList[1] = "psb_drv"; break; /* GMA600/Oaktrail */ case 0x4100: case 0x4101: case 0x4102: case 0x4103: case 0x4104: case 0x4105: case 0x4106: case 0x4107: /* Atom E620/Oaktrail */ case 0x4108: /* Medfield */ case 0x0130: case 0x0131: case 0x0132: case 0x0133: case 0x0134: case 0x0135: case 0x0136: case 0x0137: /* GMA 3600/CDV */ case 0x0be0: case 0x0be1: case 0x0be2: case 0x0be3: case 0x0be4: case 0x0be5: case 0x0be6: case 0x0be7: case 0x0be8: case 0x0be9: case 0x0bea: case 0x0beb: case 0x0bec: case 0x0bed: case 0x0bee: case 0x0bef: /* Use fbdev/vesa driver on Oaktrail, Medfield, CDV */ break; case 0x3577: case 0x2562: case 0x3582: case 0x358e: case 0x2572: case 0x2582: case 0x258a: case 0x2592: case 0x2772: case 0x27a2: case 0x27ae: case 0x2972: case 0x29b2: case 0x29c2: case 0x29d2: case 0xa001: case 0xa011: case 0x0042: /* XXX Ironlake is unstable with modesetting */ case 0x0046: /* Use intel driver on 2nd and 3rd generation */ driverList[0] = "intel"; break; default: /* Use modesetting driver on 4th generation and up */ driverList[0] = "modesetting"; break; } break; case 0x102b: driverList[0] = "mga"; break; case 0x10c8: driverList[0] = "neomagic"; break; case 0x10de: case 0x12d2: { int idx = 0; #if defined(__linux__) || defined(__NetBSD__) driverList[idx++] = "nouveau"; #endif driverList[idx++] = "nv"; break; } case 0x1106: driverList[0] = "openchrome"; break; case 0x1b36: driverList[0] = "qxl"; break; case 0x1163: driverList[0] = "rendition"; break; case 0x5333: switch (dev->device_id) { case 0x88d0: case 0x88d1: case 0x88f0: case 0x8811: case 0x8812: case 0x8814: case 0x8901: driverList[0] = "s3"; break; case 0x5631: case 0x883d: case 0x8a01: case 0x8a10: case 0x8c01: case 0x8c03: case 0x8904: case 0x8a13: driverList[0] = "s3virge"; break; default: driverList[0] = "savage"; break; } break; case 0x1039: driverList[0] = "sis"; break; case 0x126f: driverList[0] = "siliconmotion"; break; case 0x121a: if (dev->device_id < 0x0003) driverList[0] = "voodoo"; else driverList[0] = "tdfx"; break; case 0x1011: driverList[0] = "tga"; break; case 0x1023: driverList[0] = "trident"; break; case 0x100c: driverList[0] = "tseng"; break; case 0x80ee: driverList[0] = "vboxvideo"; break; case 0x15ad: driverList[0] = "vmware"; break; case 0x18ca: if (dev->device_id == 0x47) driverList[0] = "xgixp"; else driverList[0] = "xgi"; break; default: break; } for (i = 0; driverList[i] != NULL; i++) { xf86AddMatchedDriver(md, driverList[i]); } } #ifdef __linux__ static int xchomp(char *line) { size_t len = 0; if (!line) { return 1; } len = strlen(line); if (line[len - 1] == '\n' && len > 0) { line[len - 1] = '\0'; } return 0; } /* This function is used to provide a workaround for binary drivers that * don't export their PCI ID's properly. If distros don't end up using this * feature it can and should be removed because the symbol-based resolution * scheme should be the primary one */ void xf86MatchDriverFromFiles(uint16_t match_vendor, uint16_t match_chip, XF86MatchedDrivers *md) { DIR *idsdir; FILE *fp; struct dirent *direntry; char *line = NULL, *tmpMatch; size_t len; ssize_t read; char path_name[512], vendor_str[5], chip_str[5]; uint16_t vendor, chip; int j; idsdir = opendir(PCI_TXT_IDS_PATH); if (!idsdir) return; xf86Msg(X_INFO, "Scanning %s directory for additional PCI ID's supported by the drivers\n", PCI_TXT_IDS_PATH); direntry = readdir(idsdir); /* Read the directory */ while (direntry) { if (direntry->d_name[0] == '.') { direntry = readdir(idsdir); continue; } len = strlen(direntry->d_name); /* A tiny bit of sanity checking. We should probably do better */ if (strncmp(&(direntry->d_name[len - 4]), ".ids", 4) == 0) { /* We need the full path name to open the file */ snprintf(path_name, sizeof(path_name), "%s/%s", PCI_TXT_IDS_PATH, direntry->d_name); fp = fopen(path_name, "r"); if (fp == NULL) { xf86Msg(X_ERROR, "Could not open %s for reading. Exiting.\n", path_name); goto end; } /* Read the file */ #ifdef __GLIBC__ while ((read = getline(&line, &len, fp)) != -1) { #else while ((line = fgetln(fp, &len)) != (char *) NULL) { #endif /* __GLIBC __ */ xchomp(line); if (isdigit(line[0])) { strlcpy(vendor_str, line, sizeof(vendor_str)); vendor = (int) strtol(vendor_str, NULL, 16); if ((strlen(&line[4])) == 0) { chip_str[0] = '\0'; chip = -1; } else { /* Handle trailing whitespace */ if (isspace(line[4])) { chip_str[0] = '\0'; chip = -1; } else { /* Ok, it's a real ID */ strlcpy(chip_str, &line[4], sizeof(chip_str)); chip = (int) strtol(chip_str, NULL, 16); } } if (vendor == match_vendor && chip == match_chip) { tmpMatch = (char *) malloc(sizeof(char) * strlen(direntry->d_name) - 3); if (!tmpMatch) { xf86Msg(X_ERROR, "Could not allocate space for the module name. Exiting.\n"); goto end; } /* hack off the .ids suffix. This should guard * against other problems, but it will end up * taking off anything after the first '.' */ for (j = 0; j < (strlen(direntry->d_name) - 3); j++) { if (direntry->d_name[j] == '.') { tmpMatch[j] = '\0'; break; } else { tmpMatch[j] = direntry->d_name[j]; } } xf86AddMatchedDriver(md, tmpMatch); xf86Msg(X_INFO, "Matched %s from file name %s\n", tmpMatch, direntry->d_name); free(tmpMatch); } } else { /* TODO Handle driver overrides here */ } } fclose(fp); } direntry = readdir(idsdir); } end: free(line); closedir(idsdir); } #endif /* __linux__ */ void xf86PciMatchDriver(XF86MatchedDrivers *md) { struct pci_device *info = NULL; struct pci_device_iterator *iter; /* Find the primary device, and get some information about it. */ iter = pci_slot_match_iterator_create(NULL); while ((info = pci_device_next(iter)) != NULL) { if (xf86IsPrimaryPci(info)) { break; } } pci_iterator_destroy(iter); #ifdef __linux__ if (info) xf86MatchDriverFromFiles(info->vendor_id, info->device_id, md); #endif if (info != NULL) { xf86VideoPtrToDriverList(info, md); } } Bool xf86PciConfigure(void *busData, struct pci_device *pDev) { struct pci_device *pVideo = NULL; pVideo = (struct pci_device *) busData; if (pDev && (pDev->domain == pVideo->domain) && (pDev->bus == pVideo->bus) && (pDev->dev == pVideo->dev) && (pDev->func == pVideo->func)) return 0; return 1; } void xf86PciConfigureNewDev(void *busData, struct pci_device *pVideo, GDevRec * GDev, int *chipset) { char busnum[8]; char *tmp; pVideo = (struct pci_device *) busData; snprintf(busnum, sizeof(busnum), "%d", pVideo->bus); XNFasprintf(&tmp, "PCI:%s:%d:%d", busnum, pVideo->dev, pVideo->func); GDev->busID = tmp; GDev->chipID = pVideo->device_id; GDev->chipRev = pVideo->revision; if (*chipset < 0) *chipset = (pVideo->vendor_id << 16) | pVideo->device_id; } char * DRICreatePCIBusID(const struct pci_device *dev) { char *busID; if (asprintf(&busID, "pci:%04x:%02x:%02x.%d", dev->domain, dev->bus, dev->dev, dev->func) == -1) return NULL; return busID; }