/* 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 "kudzu.h" #include "ide.h" #include "parallel.h" #include "pci.h" #include "psaux.h" #include "serial.h" #include "sbus.h" #include "scsi.h" #include "keyboard.h" #ifdef _i_wanna_build_this_crap_ #include "isapnp.h" #endif #include #include #include #include #include #include "device.h" #ifndef __LOADER__ static struct { char *prefix; char *match; } fbcon_drivers[] = { /* The first string is a prefix of fix->id reported by fbcon (check linux/drivers/video for that), the latter is a shell pattern (see glob(7)) of either the desc or driver strings probed by kudzu library (for PCI you can find this in pcitable file). */ { "ATY Mach64", "*:*Mach64*" }, { "BWtwo", "Sun|Monochrome (bwtwo)" }, { "CGfourteen", "Sun|SX*" }, { "CGsix ", "Sun|*GX*" }, { "CGthree", "Sun|Color3 (cgthree)" }, { "CLgen", "Cirrus Logic|GD *" }, { "Creator", "Sun|FFB*" }, { "DEC 21030 TGA", "DEC|DECchip 21030 [TGA]" }, { "Elite 3D", "Sun|Elite3D*" }, { "Leo", "Sun|*ZX*" }, { "MATROX", "Matrox|MGA*" }, { "Permedia2", "*3DLabs*" }, { "TCX", "Sun|TCX*" }, /* { "VESA VGA", ??? }, */ /* { "VGA16 VGA", ??? }, */ { NULL, NULL }, }; #endif typedef struct device *(newFunc)(struct device *); typedef int (initFunc)(); typedef struct device *(probeFunc)(enum deviceClass, int, struct device *); struct bus { char *string; struct device *(*newFunc)(struct device *); int (*initFunc)(char *filename); void (*freeFunc)(); struct device *(*probeFunc)(enum deviceClass, int, struct device *); }; char *classStrings[] = { "UNSPEC", "OTHER", "NETWORK", "SCSI", "VIDEO", "AUDIO", "MOUSE", "MODEM", "CDROM", "TAPE", "FLOPPY", "SCANNER", "HD", "RAID", "PRINTER", "CAPTURE", "KEYBOARD", NULL }; struct bus buses[] = { { "UNSPEC", NULL, NULL, NULL, NULL }, { "OTHER", (newFunc *)newDevice, NULL, NULL, NULL }, { "PCI", (newFunc *)pciNewDevice, pciReadDrivers, pciFreeDrivers, pciProbe }, { "SBUS", (newFunc *)sbusNewDevice, NULL, NULL, sbusProbe }, #ifndef __LOADER__ { "PSAUX", (newFunc *)psauxNewDevice, NULL, NULL, psauxProbe }, { "SERIAL", (newFunc *)serialNewDevice, NULL, NULL, serialProbe }, { "PARALLEL", (newFunc *)parallelNewDevice, parallelReadDrivers, parallelFreeDrivers, parallelProbe }, { "SCSI", (newFunc *)scsiNewDevice, NULL, NULL, scsiProbe }, { "IDE", (newFunc *)ideNewDevice, NULL, NULL, ideProbe }, { "KEYBOARD", (newFunc *)keyboardNewDevice, NULL, NULL, keyboardProbe }, #ifdef _i_wanna_build_this_crap_ { "ISAPNP", (newFunc *)isapnpNewDevice, NULL, NULL, isapnpProbe }, #endif /* crap */ #endif /* LOADER */ { NULL, NULL, NULL, NULL, NULL } }; struct device *newDevice(struct device *old, struct device *new) { if (!old) { if (!new) { new = malloc(sizeof(struct device)); memset(new,'\0',sizeof(struct device)); } new->class = CLASS_UNSPEC; } else { new->class = old->class; if (old->device) new->device = strdup(old->device); if (old->driver) new->driver = strdup(old->driver); if (old->desc) new->desc = strdup(old->desc); } new->newDevice = newDevice; new->freeDevice = freeDevice; new->compareDevice = compareDevice; return new; } void freeDevice(struct device *dev) { if (!dev) { printf("freeDevice(null)\n"); abort(); /* return; */ } if (dev->device) free (dev->device); if (dev->driver) free (dev->driver); if (dev->desc) free (dev->desc); free (dev); } void writeDevice(FILE *file, struct device *dev) { int bus, i; if (!file) { printf("writeDevice(null,dev)\n"); abort(); } if (!dev) { printf("writeDevice(file,null)\n"); abort(); } if (dev->bus == BUS_UNSPEC) bus = 0; else for (bus = 1, i = dev->bus; !(i & 1); bus++, i>>=1); fprintf(file,"-\nclass: %s\nbus: %s\ndetached: %d\n", classStrings[dev->class],buses[bus].string,dev->detached); if (dev->device) fprintf(file,"device: %s\n",dev->device); fprintf(file,"driver: %s\ndesc: \"%s\"\n",dev->driver,dev->desc); } int compareDevice(struct device *dev1, struct device *dev2) { if (!dev1 || !dev2) return 1; if (dev1->class != dev2->class) return 1; if (dev1->bus != dev2->bus) return 1; if (dev1->device && dev2->device && strcmp(dev1->device,dev2->device)) return 1; /* Look - a special case! * If it's just the driver that changed, we might * want to act differently on upgrades. */ if (strcmp(dev1->driver,dev2->driver)) return 2; return 0; } #ifndef __LOADER__ struct device *readDevice(FILE *file) { char *linebuf=malloc(512); struct device *retdev=NULL, *tmpdev; int i; if (!file) { printf("readDevice(null)\n"); abort(); } memset(linebuf,'\0',512); while (strcmp(linebuf,"-")) { memset(linebuf,'\0',512); linebuf=fgets(linebuf,512,file); if (!linebuf) break; /* kill trailing \n */ (*rindex(linebuf,'\n'))='\0'; if (!strcmp(linebuf,"-")) { break; } else { if (!retdev) retdev = newDevice(NULL,NULL); } if (!strncmp(linebuf,"class:",6)) { for (i=0; classStrings[i] && strcmp(classStrings[i],linebuf+7); i++); if (classStrings[i]) retdev->class = i; else retdev->class = CLASS_OTHER; } else if (!strncmp(linebuf,"bus:",4)) { for (i=0; buses[i].string && strcmp(buses[i].string,linebuf+5); i++); if (buses[i].string) { tmpdev = (struct device *)buses[i].newFunc(retdev); retdev->freeDevice(retdev); retdev = tmpdev; } else retdev->bus = BUS_OTHER; } else if (!strncmp(linebuf,"driver:",7)) { retdev->driver = strdup(linebuf+8); } else if (!strncmp(linebuf,"detached:",9)) { retdev->detached = atoi(linebuf+10); } else if (!strncmp(linebuf,"device:",7)) { retdev->device = strdup(linebuf+8); } else if (!strncmp(linebuf,"desc:",5)) { if (rindex(linebuf,'"')!=index(linebuf,'"')) { (*rindex(linebuf,'"')) = '\0'; retdev->desc = strdup(index(linebuf,'"')+1); } else { retdev->desc = strdup(linebuf+6); } } switch (retdev->bus) { case BUS_PCI: if (!strncmp(linebuf,"vendorId:",9)) ((struct pciDevice *)retdev)->vendorId = strtol(linebuf+10, (char **) NULL, 16); else if (!strncmp(linebuf,"deviceId:",9)) ((struct pciDevice *)retdev)->deviceId = strtol(linebuf+10, (char **) NULL, 16); else if (!strncmp(linebuf,"pciType:",8)) ((struct pciDevice *)retdev)->pciType = strtol(linebuf+6, (char **) NULL, 9); break; case BUS_PARALLEL: if (!strncmp(linebuf,"pnpmodel:",9)) ((struct parallelDevice *)retdev)->pnpmodel = strdup(linebuf+10); if (!strncmp(linebuf,"pnpmfr:",7)) ((struct parallelDevice *)retdev)->pnpmfr = strdup(linebuf+8); if (!strncmp(linebuf,"pnpmodes:",9)) ((struct parallelDevice *)retdev)->pnpmodes = strdup(linebuf+10); if (!strncmp(linebuf,"pnpdesc:",8)) ((struct parallelDevice *)retdev)->pnpdesc = strdup(linebuf+9); if (!strncmp(linebuf,"pinfo.xres:",11)) ((struct parallelDevice *)retdev)->pinfo->xres = atoi(linebuf+12); if (!strncmp(linebuf,"pinfo.yres:",11)) ((struct parallelDevice *)retdev)->pinfo->yres = atoi(linebuf+12); if (!strncmp(linebuf,"pinfo.color:",12)) ((struct parallelDevice *)retdev)->pinfo->color = atoi(linebuf+13); if (!strncmp(linebuf,"pinfo.ascii:",12)) ((struct parallelDevice *)retdev)->pinfo->ascii = atoi(linebuf+13); if (!strncmp(linebuf,"pinfo.uniprint:",15)) ((struct parallelDevice *)retdev)->pinfo->uniprint = strdup(linebuf+16); break; case BUS_SERIAL: if (!strncmp(linebuf,"pnpmodel:",9)) ((struct serialDevice *)retdev)->pnpmodel = strdup(linebuf+10); if (!strncmp(linebuf,"pnpmfr:",7)) ((struct serialDevice *)retdev)->pnpmfr = strdup(linebuf+8); if (!strncmp(linebuf,"pnpcompat:",10)) ((struct serialDevice *)retdev)->pnpcompat = strdup(linebuf+11); if (!strncmp(linebuf,"pnpdesc:",8)) ((struct serialDevice *)retdev)->pnpdesc = strdup(linebuf+9); break; case BUS_SBUS: if (!strncmp(linebuf,"width:",6)) ((struct sbusDevice *)retdev)->width = atoi(linebuf+7); if (!strncmp(linebuf,"height:",7)) ((struct sbusDevice *)retdev)->height = atoi(linebuf+8); if (!strncmp(linebuf,"freq:",5)) ((struct sbusDevice *)retdev)->freq = atoi(linebuf+6); if (!strncmp(linebuf,"monitor:",8)) ((struct sbusDevice *)retdev)->monitor = atoi(linebuf+9); break; case BUS_SCSI: if (!strncmp(linebuf,"host:",5)) ((struct scsiDevice *)retdev)->host = atoi(linebuf+6); if (!strncmp(linebuf,"channel:",8)) ((struct scsiDevice *)retdev)->channel = atoi(linebuf+9); if (!strncmp(linebuf,"id:",3)) ((struct scsiDevice *)retdev)->id = atoi(linebuf+4); if (!strncmp(linebuf,"lun:",3)) ((struct scsiDevice *)retdev)->lun = atoi(linebuf+4); break; case BUS_IDE: if (!strncmp(linebuf,"physical:",9)) ((struct ideDevice *)retdev)->physical = strdup(linebuf+10); if (!strncmp(linebuf,"logical:",8)) ((struct ideDevice *)retdev)->logical = strdup(linebuf+9); break; #ifdef _i_wanna_build_this_crap_ case BUS_ISAPNP: if (!strncmp(linebuf,"pdeviceId:",10)) ((struct isapnpDevice *)retdev)->pdeviceId = strdup(linebuf+11); if (!strncmp(linebuf,"ppnpdesc:",9)) ((struct isapnpDevice *)retdev)->ppnpdesc = strdup(linebuf+10); if (!strncmp(linebuf,"deviceId:",9)) ((struct isapnpDevice *)retdev)->deviceId = strdup(linebuf+10); if (!strncmp(linebuf,"pnpdesc:",8)) ((struct isapnpDevice *)retdev)->pnpdesc = strdup(linebuf+9); if (!strncmp(linebuf,"compat:",7)) ((struct isapnpDevice *)retdev)->compat = strdup(linebuf+8); if (!strncmp(linebuf,"io:",3)) ((struct isapnpDevice *)retdev)->io = isapnpReadResources(linebuf+4,16); if (!strncmp(linebuf,"irq:",4)) ((struct isapnpDevice *)retdev)->irq = isapnpReadResources(linebuf+5,10); if (!strncmp(linebuf,"dma:",4)) ((struct isapnpDevice *)retdev)->dma = isapnpReadResources(linebuf+5,10); if (!strncmp(linebuf,"mem:",4)) ((struct isapnpDevice *)retdev)->mem = isapnpReadResources(linebuf+5,16); break; #endif default: break; } } return retdev; } #endif int initializeDeviceList() { int bus; for (bus=0;buses[bus].string;bus++) if (buses[bus].initFunc) buses[bus].initFunc(NULL); return 0; } void freeDeviceList() { int bus; for (bus=0;buses[bus].string;bus++) if (buses[bus].freeFunc) buses[bus].freeFunc(); } /* used to sort device lists by a) type, b) device, c) description */ static int devCmp( const void *a, const void *b ) { const struct device *one,*two; int x,y,z,zz; one=((const struct device **)a)[0]; two=((const struct device **)b)[0]; x=one->class - two->class; if (one->device && two->device) y=strcmp(one->device,two->device); else { y = one->device - two->device; } z=two->index - one->index; zz=strcmp(one->desc,two->desc); if (x) return x; else if (y) return y; else if (z) return z; else return zz; } #ifndef __LOADER__ struct device ** readDevices ( char *fn ) { FILE *confFile; char *linebuf; struct device *dev, **retdevs=NULL; int num=0; int index=0,x; enum deviceClass cl=CLASS_UNSPEC; linebuf=calloc(512,sizeof(char)); confFile = fopen(fn,"r"); if (!confFile) return NULL; while (strcmp(linebuf,"-\n")) { linebuf=fgets(linebuf,512,confFile); if (!linebuf) return NULL; } while (1) { dev = readDevice(confFile); if (!dev) break; retdevs = realloc (retdevs,(num+2) * sizeof (struct device *)); retdevs[num] = dev; retdevs[num+1] = NULL; num++; } fclose(confFile); qsort(retdevs, num, sizeof(struct device *), devCmp); for (x=0;retdevs[x];x++) { if (retdevs[x]->class!=cl) { index = 0; } retdevs[x]->index = index; cl = retdevs[x]->class; index++; } return retdevs; } int writeDevices ( char *fn, struct device **devlist ) { int x; FILE *confFile; if (!devlist || !devlist[0]) return 1; confFile = fopen(fn,"w"); if (!confFile) return 1; for (x=0;devlist[x];x++) { devlist[x]->writeDevice(confFile,devlist[x]); } fclose(confFile); return 0; } static void fbProbe( struct device *devices ) { FILE *procfb; int i, j; char name[4], buffer[50], *id; struct device *d; procfb = fopen("/proc/fb","r"); if (!procfb) return; while (fgets(buffer, 50, procfb) != NULL) { i = atoi (buffer); id = strchr (buffer, ' ') + 1; for (j = 0; fbcon_drivers[j].prefix; j++) if (!strncmp (id, fbcon_drivers[j].prefix, strlen (fbcon_drivers[j].prefix))) break; if (!fbcon_drivers[j].prefix) continue; for (d = devices; d; d = d->next) if (!d->device && d->class == CLASS_VIDEO && (!fnmatch (fbcon_drivers[j].match, d->desc, FNM_NOESCAPE) || !fnmatch (fbcon_drivers[j].match, d->driver, FNM_NOESCAPE))) { sprintf(name, "fb%d", i); d->device = strdup (name); } } fclose(procfb); } #endif struct device ** probeDevices ( enum deviceClass probeClass, enum deviceBus probeBus, int probeFlags ) { struct device *devices=NULL,**devlist=NULL; int numDevs=0, bus, x, index=0; enum deviceClass cl=CLASS_UNSPEC; for (bus=1;buses[bus].string;bus++) { if ((probeBus & (1 << (bus - 1))) || probeBus == BUS_UNSPEC) if (buses[bus].probeFunc) devices = buses[bus].probeFunc(probeClass, probeFlags, devices); if ((probeFlags & PROBE_ONE) && (devices)) break; } if (devices == NULL) return NULL; #ifndef __LOADER__ if (probeClass==CLASS_VIDEO || probeClass == CLASS_UNSPEC) fbProbe(devices); #endif while (devices) { devlist=realloc(devlist, (numDevs+2) * sizeof(struct device *)); devlist[numDevs]=devices; devlist[numDevs+1]=NULL; numDevs++; devices=devices->next; } qsort(devlist, numDevs, sizeof(struct device *), devCmp); for (x=0;devlist[x];x++) { if (devlist[x]->class!=cl) { index = 0; } devlist[x]->index = index; cl = devlist[x]->class; index++; } return devlist; } #ifndef __LOADER__ int listCompare( struct device **list1, struct device **list2, struct device ***retlist1, struct device ***retlist2) { struct device *curr1, *prev1, *curr2, *prev2, *head1, *head2; struct device **ret1=NULL, **ret2=NULL; int x, notfound=1; /* Turn arrays into lists. */ for (x=0;list1[x];x++) { list1[x]->next = list1[x+1]; } for (x=0;list2[x];x++) { list2[x]->next = list2[x+1]; } curr1 = head1 = list1[0]; head2 = list2[0]; prev1 = NULL; while (curr1) { curr2 = head2; prev2 = NULL; while (curr2) { if (!(notfound=curr1->compareDevice(curr1,curr2))) { if (!prev1) head1 = curr1->next; else prev1->next = curr1->next; if (!prev2) head2 = curr2->next; else prev2->next = curr2->next; break; } else { prev2 = curr2; curr2 = curr2->next; } } if (notfound) prev1 = curr1; curr1 = curr1 ->next; } /* Generate return lists */ if (retlist1) { curr1 = head1; ret1=malloc(sizeof(struct device *)); ret1[0]=NULL; for(x=0;curr1;x++) { ret1=realloc(ret1,(x+2)*sizeof(struct device *)); ret1[x]=curr1; curr1=curr1->next; } ret1[x]=NULL; (*retlist1)=ret1; } if (retlist2) { curr2 = head2; ret2=malloc(sizeof(struct device *)); ret2[0]=NULL; for(x=0;curr2;x++) { ret2=realloc(ret2,(x+2)*sizeof(struct device *)); ret2[x]=curr2; curr2=curr2->next; } ret2[x]=NULL; (*retlist2)=ret2; } if (head1 || head2 ) return 1; else return 0; } #endif