/* Copyright 1999 Red Hat, Inc. * * This software may be freely redistributed under the terms of the GNU * public license. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include #include #include #include #include "pci.h" struct pciDevice * pciDeviceList = NULL; static int numPciDevices = 0; static struct pci_access *pacc=NULL; static jmp_buf pcibuf; static int devCmp(const void * a, const void * b) { const struct pciDevice * one = a; const struct pciDevice * two = b; int x=0,y=0; x = (one->vendorId - two->vendorId); y = (one->deviceId - two->deviceId); if (x) return x; else return y; } static void pciFreeDevice(struct pciDevice *dev) { freeDevice((struct device *)dev); } static void pciWriteDevice(FILE *file, struct pciDevice *dev) { writeDevice(file, (struct device *)dev); fprintf(file,"vendorId: %04x\ndeviceId: %04x\n",dev->vendorId,dev->deviceId); } static int pciCompareDevice(struct pciDevice *dev1, struct pciDevice *dev2) { int x=compareDevice((struct device *)dev1,(struct device *)dev2); if (x) return x; return devCmp( (void *)dev1, (void *)dev2 ); } struct pciDevice * pciNewDevice(struct pciDevice *dev) { struct pciDevice *ret; ret = malloc(sizeof(struct pciDevice)); memset(ret,'\0',sizeof(struct pciDevice)); ret=(struct pciDevice *)newDevice((struct device *)dev,(struct device *)ret); ret->bus = BUS_PCI; if (dev && dev->bus == BUS_PCI) { ret->vendorId = dev->vendorId; ret->deviceId = dev->deviceId; } ret->newDevice = pciNewDevice; ret->freeDevice = pciFreeDevice; ret->writeDevice = pciWriteDevice; ret->compareDevice = pciCompareDevice; return ret; } static int vendCmp(const void * a, const void * b) { const struct pciDevice * one = a; const struct pciDevice * two = b; return (one->vendorId - two->vendorId); } static unsigned int kudzuToPci(enum deviceClass class) { switch (class) { case CLASS_UNSPEC: return 0; case CLASS_OTHER: return 0; case CLASS_NETWORK: return PCI_BASE_CLASS_NETWORK; case CLASS_VIDEO: return PCI_BASE_CLASS_DISPLAY; case CLASS_AUDIO: return PCI_CLASS_MULTIMEDIA_AUDIO; case CLASS_SCSI: return PCI_CLASS_STORAGE_SCSI; case CLASS_FLOPPY: return PCI_CLASS_STORAGE_FLOPPY; case CLASS_RAID: return PCI_CLASS_STORAGE_RAID; case CLASS_MOUSE: /* !?!? */ return PCI_CLASS_INPUT_MOUSE; default: return 0; } } static enum deviceClass pciToKudzu(unsigned int class) { if (!class) return CLASS_UNSPEC; switch (class >> 8) { case PCI_BASE_CLASS_NETWORK: return CLASS_NETWORK; case PCI_BASE_CLASS_DISPLAY: return CLASS_VIDEO; default: break; } switch (class) { case PCI_CLASS_STORAGE_SCSI: return CLASS_SCSI; case PCI_CLASS_STORAGE_FLOPPY: return CLASS_FLOPPY; case PCI_CLASS_STORAGE_RAID: return CLASS_RAID; case PCI_CLASS_MULTIMEDIA_AUDIO: return CLASS_AUDIO; case PCI_CLASS_INPUT_MOUSE: return CLASS_MOUSE; default: return CLASS_OTHER; } } char *getVendor(unsigned int vendor) { struct pciDevice *searchDev, key; char *tmpstr; key.vendorId = vendor; searchDev = bsearch(&key,pciDeviceList,numPciDevices, sizeof(struct pciDevice), vendCmp); if (searchDev) { int x; x=strchr(searchDev->desc,'|')-searchDev->desc; tmpstr=calloc(x,sizeof(char)); tmpstr=strncpy(tmpstr,searchDev->desc,x); return tmpstr; } else { return NULL; } } int pciProbeReadDrivers() { int fd; struct stat sb; char * buf; int numDrivers; char * start; struct pciDevice * nextDevice; char module[5000]; char descrip[5000]; fd = open("/usr/share/kudzu/pcitable", O_RDONLY); if (fd < 0) fd = open("/etc/pcitable", O_RDONLY); if (fd < 0) fd = open("/modules/pcitable", O_RDONLY); if (fd < 0) fd = open("./pcitable", O_RDONLY); if (fd < 0) return -1; fstat(fd, &sb); buf = alloca(sb.st_size + 1); read(fd, buf, sb.st_size); buf[sb.st_size] = '\0'; close(fd); /* upper bound */ numDrivers = 1; start = buf; while ((start = strchr(start, '\n'))) { numDrivers++; start++; } pciDeviceList = realloc(pciDeviceList, sizeof(*pciDeviceList) * (numPciDevices + numDrivers)); nextDevice = pciDeviceList + numPciDevices; start = buf; while (start && *start) { while (isspace(*start)) start++; if (*start != '#' && *start != '\n') { if (sscanf(start, "%x %x \"%[^\"]\" \"%[^\"]", &nextDevice->vendorId, &nextDevice->deviceId, module, descrip ) == 4) { numPciDevices++; nextDevice->driver = strdup(module); nextDevice->desc = strdup(descrip); nextDevice->next = NULL; nextDevice->device = NULL; nextDevice->class = 0; nextDevice->bus = BUS_PCI; nextDevice++; } } start = strchr(start, '\n'); if (start) start++; } qsort(pciDeviceList, numPciDevices, sizeof(*pciDeviceList), devCmp); return 0; } struct pciDevice * pciGetDeviceInfo(unsigned int vend, unsigned int dev) { struct pciDevice *searchDev, key; key.vendorId = vend; key.deviceId = dev; searchDev = bsearch(&key,pciDeviceList,numPciDevices, sizeof(struct pciDevice), devCmp); if (!searchDev) { char *namebuf; searchDev = pciNewDevice(NULL); searchDev->vendorId = vend; searchDev->deviceId = dev; searchDev->driver = strdup("unknown"); searchDev->desc = calloc(128, sizeof(char)); namebuf = getVendor(vend); if (!namebuf) { snprintf(searchDev->desc,128, "Unknown vendor|unknown device %04x:%04x", searchDev->vendorId, searchDev->deviceId); } else { snprintf(searchDev->desc,128, "%s|unknown device %04x:%04x", namebuf, searchDev->vendorId, searchDev->deviceId); } } else { searchDev = pciNewDevice(searchDev); } return searchDev; } static void pcinull(char * foo, ...) { } static void pcibail(char * foo, ...) { longjmp(pcibuf,1); } struct device * pciProbe(enum deviceClass probeClass, int probeFlags, struct device *devlist) { struct pci_dev *p; unsigned int type = kudzuToPci(probeClass),devtype; int order=0; if ((probeClass==CLASS_UNSPEC) || (probeClass==CLASS_OTHER) || (probeClass==CLASS_NETWORK) || (probeClass==CLASS_SCSI) || (probeClass==CLASS_VIDEO) || (probeClass==CLASS_AUDIO) || (probeClass==CLASS_MODEM) || (probeClass==CLASS_RAID)) { pacc = pci_alloc(); if (!pacc) return devlist; pacc->debug=pacc->warning=pcinull; pacc->error=pcibail; if (!setjmp(pcibuf)) { pci_init(pacc); pci_scan_bus(pacc); for (p = pacc->devices; p; p=p->next) { byte config[256]; int x=64; struct pciDevice *dev,*a_dev; memset(config,'\0',256); pci_read_block(p, 0, config, x); if (x<128 && (config[PCI_HEADER_TYPE] & 0x7f) == PCI_HEADER_TYPE_CARDBUS) { pci_read_block(p, 0, config+64, 64); x=128; } dev = pciGetDeviceInfo(p->vendor_id,p->device_id); devtype = config[PCI_CLASS_DEVICE+1] << 8 | config[PCI_CLASS_DEVICE]; if ( (probeFlags & PROBE_ALL) || (strcmp(dev->driver,"unknown") && strcmp(dev->driver,"ignore"))) { if (!type || (type<0xff && (type==devtype>>8)) || (type==devtype)) { a_dev = pciNewDevice(dev); a_dev->class = pciToKudzu(devtype); a_dev->index = order; order++; if (devlist) { a_dev->next = devlist; } devlist = (struct device *)a_dev; } } pciFreeDevice(dev); } pci_cleanup(pacc); } } return devlist; }