/* very simple set of routines to get back what is stored in /proc/pci in */ /* a programmer friendly fashion. Also tries to map a pci entry into a */ /* kernel module name to aid autoloading modules during an install */ /* Michael Fulbright Sept 1997 */ #include #include #include #include #include #include #include "pciprobe.h" #include "pci-ids.h" /* clean up that string */ static void TrimWhitespace( char *s ) { char *f, *l, *p, *q; if (!(*s)) return; for (f=s; *f && isspace(*f); f++) ; if (!*f) { *s = '\0'; return; } for (l=f+strlen(f)-1; isspace(*l) ; l--) *l = '\0'; q = s, p = f; while (*p) *q++ = *p++; *q = '\0'; } /* create a new clean pciDevice entry */ struct pciDevice *pciNewDevice( void ) { struct pciDevice *p; p = (struct pciDevice *) malloc( sizeof(struct pciDevice) ); p->nhits = 0; p->name=NULL; p->module=NULL; p->class=PCI_UNSET; return p; } /* remove a pciDevice */ void pciFreeDevice( struct pciDevice *p ) { int i; if (p->name) { for (i=0; inhits; i++) free(p->name[i]); free(p->name); } if (p->module) { for (i=0; inhits; i++) free(p->module[i]); free(p->module); } free(p); } /* given a class to probe, returns an array of devices found which match */ /* all entries are malloc'd, so caller must free when done. Use */ /* pciDeviceFree() to free individual pciDevice's, since they have */ /* internal elements which are malloc'd */ /* returns number cards found. If nothing found returns 0 and */ /* setsdevs is set to NULL */ /* if system error occurs (like no /proc/pci!) returns -1 */ int pciProbeDevice( enum pciClass type, struct pciDevice ***devs ) { int done; int found, maxlen, maxidx; int ok, first; int ndevs, nmatches; int bus, device, function; int nonvgadev, madeentry; int i, len; char *devtype, *devmodel, *vendorid, *deviceid, *mapped, *mapname, *eptr; int stage; unsigned int devnumid, vennumid; FILE *f; char buf[100]; struct pci_module_map *pcidb; /* open /proc/pci and parse output */ f=fopen("/proc/pci", "r"); if (!f) { return -1; } done = 0; stage = 0; ndevs = 0; *devs = NULL; while (!done) { if (!fgets(buf,100,f)) break; madeentry = 0; /* havent made nothin yet */ /* see what we're looking for */ if (stage == 0) { /* need to find start of list of PCI devices */ if (!strncmp(buf, "PCI devices found:", 18)) stage++; continue; } else if (stage == 1) { /* looking for a line starting with 'Bus' */ if (strstr(buf, "Bus") && strstr(buf, "device")) { /* found it, now parse out stuff we care about */ sscanf(buf, " Bus %d, device %d, function %d:\n", &bus, &device, &function); /* read the next line for other goodies */ if (!fgets(buf,100,f)) break; /* strip out devtype and model of card */ devmodel = strchr(buf, ':'); if (!devmodel) continue; *devmodel++ = 0; /* blow revision number off end of devmodel */ if ((eptr=strrchr(devmodel, '('))) *eptr = 0; devtype=strdup(buf); devmodel=strdup(devmodel); TrimWhitespace(devtype); TrimWhitespace(devmodel); /* see if its the class we're looking for */ /* nonvgadev tells use card didnt set a class */ /* so we have to see if it happens to match a */ /* entry in the class db we have */ ok = 1; nonvgadev = strstr(devtype, CLASS_NONE) != NULL; switch (type) { case PCI_ETHERNET: ok = strstr(devtype, CLASS_ETHERNET) != NULL || nonvgadev; break; case PCI_SCSI: ok = strstr(devtype, CLASS_SCSI) != NULL || nonvgadev; break; case PCI_VIDEO: ok = strstr(devtype, CLASS_VIDEO) != NULL || strstr(devtype, CLASS_VIDEO_OTHER) != NULL || nonvgadev; break; default: ok = 0; break; } if (!ok) { free(devtype); free(devmodel); continue; } /* see if its an unknown model */ deviceid = vendorid = NULL; if (strstr(devmodel, "Unknown")) { if (!fgets(buf,100,f)) break; if (strstr(buf, "Vendor id")) { char *s, *t, *v; s = strchr(buf, '='); if (s) { s++; t = strchr(s, '.'); if (t) { *t = 0; t = strchr(t+1, '='); if (t) { t++; v = strchr(t, '.'); if (v) { *v = 0; vendorid = strdup(s); deviceid = strdup(t); TrimWhitespace(vendorid); TrimWhitespace(deviceid); devnumid=strtol(deviceid, &eptr, 16); vennumid=strtol(vendorid, &eptr, 16); } } } } } } /* now find all matches and stick into entry */ switch (type) { case PCI_ETHERNET: pcidb = eth_pci_ids; len = eth_num_ids; break; case PCI_SCSI: pcidb = scsi_pci_ids; len = scsi_num_ids; break; case PCI_VIDEO: pcidb = video_pci_ids; len = video_num_ids; break; default: pcidb = NULL; len = 0; } /* gotta find the maximally matching entry(s) */ maxlen = -1; if (!strstr(devmodel,"Unknown")) { for (i=0; i maxlen) { maxlen = l; } } } } first = 1; nmatches = 0; i = 0; while ( 1 ) { found = 0; if (strstr(devmodel, "Unknown")) { for (; i= maxlen) { maxidx = i; found = 1; break; } } } } if (!found) { if (!first || nonvgadev) break; else { mapped = "UNKNOWN"; mapname = "UNKNOWN"; } } else { mapped = (char *) pcidb[maxidx].module; mapname = (char *) pcidb[maxidx].name; } first = 0; /* insert into current pciDevice */ /* we got enough info, let make a device entry */ if (!madeentry) { if ((ndevs % 4) == 0) *devs=(struct pciDevice **) realloc(*devs,(ndevs+4)*sizeof(struct pciDevice *)); (*devs)[ndevs]=pciNewDevice(); } madeentry = 1; (*devs)[ndevs]->name=realloc((*devs)[ndevs]->name, (nmatches+1)*sizeof(char *)); (*devs)[ndevs]->module=realloc((*devs)[ndevs]->module, (nmatches+1)*sizeof(char *)); (*devs)[ndevs]->name[nmatches]=strdup(mapname); (*devs)[ndevs]->module[nmatches]=strdup(mapped); i++; nmatches++; } if (vendorid) free(vendorid); if (deviceid) free(deviceid); free(devtype); free(devmodel); if (madeentry) { (*devs)[ndevs]->nhits=nmatches; (*devs)[ndevs]->class=type; ndevs++; } } } } fclose(f); return ndevs; }