#include #include #include #include #include #include #include #include #include #include #include "devices.h" #include "install.h" #include "log.h" #include "net.h" #include "perror.h" #include "run.h" #include "scsi.h" #include "windows.h" #define MODULES_PATH "/modules/" struct devnum { char * name; short major, minor; }; const static struct devnum devices[] = { { "aztcd", 29, 0 }, { "cdu31a", 15, 0 }, { "cdu535", 24, 0 }, { "cm206cd", 32, 0 }, { "fd0", 2, 0 }, { "fd1", 2, 1 }, { "gscd", 16, 0 }, { "mcd", 23, 0 }, { "optcd", 17, 0 }, { "sbpcd", 25, 0 }, { "scd0", 11, 0 }, { "scd1", 11, 1 }, { "sjcd", 18, 0 }, }; const int numDevices = sizeof(devices) / sizeof(struct devnum); struct moduleOptions { char * arg; char * desc; char * defaults; } ; const struct moduleOptions neOptions[] = { { "io", "Base IO port:", "0x300:0x280:0x320:0x340:0x360" }, { "irq", "IRQ level:", NULL }, { NULL, NULL, NULL } } ; const struct moduleOptions de4x5Options[] = { { "io", "Base IO port:", "0x0b" }, { NULL, NULL, NULL } } ; const struct moduleOptions cdu31aOptions[] = { { "cdu31a", "IO base, IRQ, PAS?:", "" }, { NULL, NULL, NULL } } ; const struct moduleOptions cm206Options[] = { { "cm206", "IO base, IRQ:", "" }, { NULL, NULL, NULL } } ; const struct moduleOptions mcdOptions[] = { { "mcd", "IO base address:", "" }, { NULL, NULL, NULL } } ; const struct moduleOptions optcdOptions[] = { { "optcd", "IO base address:", "" }, { NULL, NULL, NULL } } ; const struct moduleOptions sbpcdOptions[] = { { "sbpcd", "IO base, IRQ, label:", "" }, { NULL, NULL, NULL } } ; #define MODULE_AUTOPROBE (1 << 0) #define MODULE_FAKEAUTOPROBE (1 << 1) struct moduleInfo { char * name; int shouldAutoprobe; const struct moduleOptions * options; int flags; char * defaultOptions; } ; /* keep this alphabetical! */ struct moduleInfo modules[] = { { "8390", 1, NULL, 0, NULL }, { "cdu31a", 0, cdu31aOptions, 0, NULL }, { "cm206", 0, cm206Options, 0, NULL }, { "de4x5", 1, de4x5Options, MODULE_AUTOPROBE, "io=0" }, { "ds", 1, NULL, 0, NULL }, { "i82365", 1, NULL, 0, NULL }, { "loop", 1, NULL, 0, NULL }, { "mcd", 0, mcdOptions, 0, NULL }, { "ne", 0, neOptions, MODULE_FAKEAUTOPROBE, "io=0x300" }, { "optcd", 0, optcdOptions, 0, NULL }, { "pcmcia_core", 1, NULL, 0, NULL }, { "sbpcd", 1, sbpcdOptions, 0, NULL }, { "tcic", 1, NULL, 0, NULL }, { NULL, 0, NULL, 0, NULL } /* sentinel */ } ; struct driver { char * name; char * modules; int isLoaded; driverOkayFn okay; enum driverTypes type; }; static int checkEthernetDev(struct driver * dev); static int checkSCSIDev(struct driver * dev); static struct driver drivers[] = { { "3com 3c509", "3c509", 0, checkEthernetDev, DRIVER_ETHERNET }, { "3com 3c59x (Vortex)", "3c59x", 0, checkEthernetDev, DRIVER_ETHERNET }, { "3com 3c501", "3c501", 0, checkEthernetDev, DRIVER_ETHERNET }, { "3com 3c503", "8390:3c503", 0, checkEthernetDev, DRIVER_ETHERNET }, { "Apricot 82596", "apricot", 0, checkEthernetDev, DRIVER_ETHERNET }, { "Cabletron E2100", "8390:e2100", 0, checkEthernetDev, DRIVER_ETHERNET }, { "Digital 425,434,435,450,500", "de4x5", 0, checkEthernetDev, DRIVER_ETHERNET }, { "Digital DEPCA and EtherWORKS", "depca", 0, checkEthernetDev, DRIVER_ETHERNET }, { "Digital EtherWORKS 3", "ewrk3", 0, checkEthernetDev, DRIVER_ETHERNET }, { "Digital 21040 (Tulip)", "tulip", 0, checkEthernetDev, DRIVER_ETHERNET }, { "D-Link DE-600 pocket adapter", "de600", 0, checkEthernetDev, DRIVER_ETHERNET }, { "D-Link DE-620 pocket adapter", "de620", 0, checkEthernetDev, DRIVER_ETHERNET }, { "HP10/100VG any LAN ", "8390:hp", 0, checkEthernetDev, DRIVER_ETHERNET }, { "HP LAN", "hp", 0, checkEthernetDev, DRIVER_ETHERNET }, { "HP PCLAN/plus", "8390:hp-plus", 0, checkEthernetDev, DRIVER_ETHERNET }, { "Intel EtherExpress", "eexpress", 0, checkEthernetDev, DRIVER_ETHERNET }, { "NE2000 and compatible", "8390:ne", 0, checkEthernetDev, DRIVER_ETHERNET }, { "SMC 9000 series", "smc9194", 0, checkEthernetDev, DRIVER_ETHERNET }, { "SMC Ultra ethernet", "8390:smc-ultra", 0, checkEthernetDev, DRIVER_ETHERNET }, { "WD8003, WD8013 and compatible", "8390:wd", 0, checkEthernetDev, DRIVER_ETHERNET }, { "Adaptec 152x", "aha152x", 0, checkSCSIDev, DRIVER_SCSI }, { "Adaptec 1542", "aha1542", 0, checkSCSIDev, DRIVER_SCSI }, { "Adaptec 1740", "aha1740", 0, checkSCSIDev, DRIVER_SCSI }, { "Adaptec 2740, 2840, 2940", "aic7xxx", 0, checkSCSIDev, DRIVER_SCSI }, { "AdvanSys Adapters", "advansys", 0, checkSCSIDev, DRIVER_SCSI }, { "Always IN2000", "in2000", 0, checkSCSIDev, DRIVER_SCSI }, { "Buslogic Adapters", "BusLogic", 0, checkSCSIDev, DRIVER_SCSI }, { "DTC 3180/3280", "dtc", 0, checkSCSIDev, DRIVER_SCSI }, { "EATA DMA Adapters", "eata_dma", 0, checkSCSIDev, DRIVER_SCSI }, { "EATA PIO Adapters)", "eata_pio", 0, checkSCSIDev, DRIVER_SCSI }, { "Future Domain TMC-885, TMC-950", "seagate", 0, checkSCSIDev, DRIVER_SCSI }, { "Future Domain TMC-16x0", "fdomain", 0, checkSCSIDev, DRIVER_SCSI }, { "Iomega PPA3 (parallel port Zip)", "ppa", 0, checkSCSIDev, DRIVER_SCSI }, { "NCR 5380", "g_NCR5380", 0, checkSCSIDev, DRIVER_SCSI }, { "NCR 53c406a", "NCR53c406a", 0, checkSCSIDev, DRIVER_SCSI }, { "NCR 53C810/53C820 PCI", "53c7,8xx", 0, checkSCSIDev, DRIVER_SCSI }, { "Pro Audio Spectrum/Studio 16", "pas16", 0, checkSCSIDev, DRIVER_SCSI }, { "Qlogic FAS", "qlogicfas", 0, checkSCSIDev, DRIVER_SCSI }, { "Qlogic ISP", "qlogicisp", 0, checkSCSIDev, DRIVER_SCSI }, { "Seagate ST01/02", "seagate", 0, checkSCSIDev, DRIVER_SCSI }, { "Trantor T128/T128F/T228", "seagate", 0, checkSCSIDev, DRIVER_SCSI }, { "UltraStor 14F/34F", "u14-34f", 0, checkSCSIDev, DRIVER_SCSI }, { "UltraStor 14F/24F/34F", "ultrastor", 0, checkSCSIDev, DRIVER_SCSI }, { "Western Digital wd7000", "wd7000", 0, checkSCSIDev, DRIVER_SCSI }, { "PCMCIA core support", "pcmcia_core", 0, NULL, DRIVER_PCMCIA }, { "PCMCIA card support", "ds", 0, NULL, DRIVER_PCMCIA }, { "PCMCIA i82365 controller", "i82365", 0, NULL, DRIVER_PCMCIA }, { "PCMCIA tcic controller", "tcic", 0, NULL, DRIVER_PCMCIA }, { "Aztech CD", "aztcd", 0, NULL, DRIVER_CDROM }, { "Goldstar R420", "gscd", 0, NULL, DRIVER_CDROM }, { "Mitsumi", "mcd", 0, NULL, DRIVER_CDROM }, { "Optics Storage 8000", "optcd", 0, NULL, DRIVER_CDROM }, { "Phillips CM206/CM260", "cm206", 0, NULL, DRIVER_CDROM }, { "Sanyo", "sjcd", 0, NULL, DRIVER_CDROM }, { "Sony CDU-31A", "cdu31a", 0, NULL, DRIVER_CDROM }, { "Sony CDU-5xx", "sonycd535", 0, NULL, DRIVER_CDROM }, { "SoundBlaster/Panasonic", "sbpcd", 0, NULL, DRIVER_CDROM }, { "Loopback device", "loop", 0, NULL, DRIVER_OTHER }, { NULL, NULL, 0, NULL, DRIVER_OTHER }, /* sentinel */ }; static const int numDrivers = sizeof(drivers) / sizeof(struct driver); static int loadDeviceModule(struct driver * driver, struct driversLoaded ** drlist); static int getOptions(const char * name, int * argcp, char *** argvp); static struct driversLoaded * allocDL(struct driversLoaded ** drlist); int devMakeInode(char * name, char * path) { int i; int major, minor; if (name[0] == 's' && name[1] == 'd') { major = 8; minor = (name[2] - 'a') << 4; if (name[3] && name[4]) minor += 10 + (name[4] - '0'); else if (name[3]) minor += (name[3] - '0'); } else if (name[0] == 'h' && name[1] == 'd') { if (name[2] == 'a') major = 3, minor = 0; else if (name[2] == 'b') major = 3, minor = 64; else if (name[2] == 'c') major = 22, minor = 0; else if (name[2] == 'd') major = 22, minor = 64; else if (name[2] == 'e') major = 33, minor = 0; else if (name[2] == 'f') major = 33, minor = 64; else if (name[2] == 'g') major = 34, minor = 0; else if (name[2] == 'h') major = 34, minor = 64; else return INST_ERROR; if (name[3] && name[4]) minor += 10 + (name[4] - '0'); else if (name[3]) minor += (name[3] - '0'); } else { for (i = 0; i < numDevices; i++) { if (!strcmp(devices[i].name, name)) break; } if (i == numDevices) return INST_ERROR; major = devices[i].major; minor = devices[i].minor; } logMessage("making device %s (%d, %d) as %s", name, major, minor, path); if (testing) messageWindow("mknod", "making device %s (%d, %d) as %s", name, major, minor, path); unlink(path); if (mknod(path, S_IFBLK | 0600, makedev(major, minor))) { messageWindow("Error", perrorstr("mknod() failed")); return INST_ERROR; } return 0; } void devRemoveInode(char * path) { logMessage("removing device file %s", path); unlink(path); } static int modulesPanel(enum driverTypes type, struct driver ** drvptr) { int drCount = 0; int i; newtComponent label, f, listbox; newtComponent okay, answer, cancel; for (i = 0; i < numDrivers; i++) { if (drivers[i].type == type) drCount++; } newtOpenWindow(17, 4, 45, 15, "Load module"); f = newtForm(NULL, NULL, 0); label = newtLabel(1, 1, "Which driver should I try?"); newtFormAddComponent(f, label); listbox = newtListbox(5, 3, 6, NEWT_LISTBOX_RETURNEXIT); drCount = 0; for (i = 0; i < numDrivers; i++) { if (drivers[i].type == type) { newtListboxAddEntry(listbox, drivers[i].name, drivers + i); drCount++; } } newtFormAddComponent(f, listbox); okay = newtButton(8, 10, "Ok"); cancel = newtButton(28, 10, "Cancel"); newtFormAddComponents(f, okay, cancel, NULL); answer = newtRunForm(f); if (answer == cancel) { newtFormDestroy(f); newtPopWindow(); return INST_CANCEL; } *drvptr = newtListboxGetCurrent(listbox); newtFormDestroy(f); newtPopWindow(); return 0; } int loadDeviceDriver(enum driverTypes type, struct driversLoaded ** drlist) { struct driver * driver; int rc; do { rc = modulesPanel(type, &driver); if (rc) return rc; rc = loadDeviceModule(driver, drlist); if (rc == INST_ERROR) { errorWindow("I can't find the device anywhere on your system!"); } } while (rc); return 0; } static int loadDeviceModule(struct driver * driver, struct driversLoaded ** drlist) { char * start, * chptr, ** modStack; char moduleName[100]; int rc; int nummods = 1; chptr = start = driver->modules; while (*chptr) { if (*chptr == ':') nummods++; chptr++; } modStack = alloca(sizeof(char *) * (nummods + 1)); nummods = 0; while (start && *start) { chptr = strchr(start, ':'); if (chptr) { strncpy(moduleName, start, chptr - start); moduleName[chptr - start] = '\0'; start = chptr + 1; } else { strcpy(moduleName, start); start = NULL; } if ((rc = loadModule(moduleName, driver->type, drlist))) { while (nummods) { removeModule(modStack[--nummods]); } return rc; } modStack[nummods] = alloca(strlen(moduleName) + 1); strcpy(modStack[nummods++], moduleName); } if (driver->okay && !driver->okay(driver)) { while (nummods) { removeModule(modStack[--nummods]); } logMessage("device check function failed to find device"); return INST_ERROR; } return 0; } int removeModule(char * module) { char * argv[] = { "/bin/rmmod", NULL, NULL }; argv[1] = module; return runProgram(RUN_LOG, "/bin/rmmod", argv); } int loadModule(char * modName, enum driverTypes type, struct driversLoaded ** drlist) { struct driversLoaded * dl; char * objName; char ** argv; int argc; int rc; int clearWindow = 0; if (type == DRIVER_SCSI) { winStatus(35, 3, "SCSI", "Scanning SCSI bus..."); clearWindow = 1; } objName = alloca(strlen(modName) + 5 + strlen(MODULES_PATH)); strcpy(objName, MODULES_PATH); strcat(objName, modName); strcat(objName, ".o"); argc = 2; argv = malloc((argc + 1) * sizeof(char *)); argv[0] = "/bin/insmod"; argv[1] = objName; argv[2] = NULL; if ((rc = getOptions(modName, &argc, &argv))) { free(argv); if (clearWindow) newtPopWindow(); return rc; } if (runProgram(RUN_LOG, "/bin/insmod", argv)) { free(argv); logMessage("insmod failed!"); if (clearWindow) newtPopWindow(); return INST_ERROR; } if (drlist) { dl = allocDL(drlist); dl->type = type; dl->argv = argv; dl->argc = argc; dl->module = strdup(modName); } if (clearWindow) newtPopWindow(); return 0; } static struct driversLoaded * allocDL(struct driversLoaded ** drlist) { struct driversLoaded * new; if (*drlist == NULL) { *drlist = malloc(sizeof(**drlist)); new = *drlist; } else { new = *drlist; while (new->next) new = new->next; new->next = malloc(sizeof(**drlist)); new = new->next; } new->next = NULL; return new; } #define OPTIONS_SPECIFY ((void *) 1) #define OPTIONS_DEFAULT ((void *) 2) static int getOptions(const char * name, int * argcp, char *** argvp) { newtComponent form, listbox, text, okay, answer, cancel; char ** parameters = NULL; char * miscParameters; char buf[2000]; struct moduleInfo * mod; void * choice; int numOptions, col, miscRow, buttonRow, i; const struct moduleOptions * option; char * miscText; char * chptr, * start; mod = modules; while (mod->name) { if (!strcmp(mod->name, name)) break; mod++; } if (!mod->name) mod = NULL; if (mod && !mod->options) { (*argcp)++; (*argvp) = realloc(*argvp, (*argcp + 1) * sizeof(char *)); (*argvp)[(*argcp) - 1] = mod->defaultOptions; (*argvp)[(*argcp)] = NULL; if (!mod->defaultOptions) (*argcp)--; return 0; } if (!mod || mod->shouldAutoprobe) { sprintf(buf, "In some cases, the %s driver needs to have extra " "information to work properly, although it normally works " "fine without. Would you like to specify extra options " "for it or allow the driver to probe your machine for the " "information it needs? Occasionally, probing will hang a " "computer, but it should not cause any damage.", name); newtOpenWindow(10, 4, 60, 16, "Module Options"); listbox = newtListbox(20, 9, 0, NEWT_LISTBOX_RETURNEXIT); newtListboxAddEntry(listbox, "Autoprobe", OPTIONS_DEFAULT); newtListboxAddEntry(listbox, "Specify options", OPTIONS_SPECIFY); buttonRow = 12; } else { sprintf(buf, "In many cases, the %s driver needs to be provided with " "extra information on your hardware. If you prefer, " "some common values for those parameters will be tried. " "This process can hang a machine, although it should " "not cause any damage.", name); newtOpenWindow(10, 5, 60, 14, "Module Options"); listbox = newtListbox(20, 7, 0, NEWT_LISTBOX_RETURNEXIT); newtListboxAddEntry(listbox, "Specify options", OPTIONS_SPECIFY); newtListboxAddEntry(listbox, "Autoprobe", OPTIONS_DEFAULT); buttonRow = 10; } text = newtTextbox(1, 1, 55, 7, NEWT_TEXTBOX_WRAP); newtTextboxSetText(text, buf); okay = newtButton(13, buttonRow, "Ok"); cancel = newtButton(39, buttonRow, "Cancel"); form = newtForm(NULL, NULL, 0); newtFormAddComponents(form, text, listbox, okay, cancel, NULL); answer = newtRunForm(form); newtPopWindow(); choice = newtListboxGetCurrent(listbox); newtFormDestroy(form); if (answer == cancel) { return INST_CANCEL; } if (choice == OPTIONS_DEFAULT) { (*argcp)++; (*argvp) = realloc(*argvp, (*argcp + 1) * sizeof(char *)); if (mod) (*argvp)[(*argcp) - 1] = mod->defaultOptions; (*argvp)[(*argcp)] = NULL; if (!mod || !mod->defaultOptions) (*argcp)--; return 0; } form = newtForm(NULL, NULL, 0); newtFormAddComponent(form, newtLabel(1, 1, "Module options:")); numOptions = 0; col = 0; if (mod) { option = mod->options; while (option->arg) { newtFormAddComponent(form, newtLabel(3, 3 + numOptions, option->desc)); if (strlen(option->desc) > col) col = strlen(option->desc); numOptions++; option++; } miscText = "Miscellaneous options:"; } else { miscText = "Module options:"; } if (numOptions) miscRow = 4 + numOptions; else miscRow = 3; newtFormAddComponent(form, newtLabel(3, miscRow, "Miscellaneous options:")); if (22 > col) col = 22; if (numOptions) { parameters = alloca(sizeof(*parameters) * numOptions); numOptions = 0; option = mod->options; while (option->arg) { sprintf(buf, "%s=", option->arg); newtFormAddComponent(form, newtEntry(col + 5, 3 + numOptions, buf, 20, parameters + numOptions, NEWT_ENTRY_SCROLL)); numOptions++; option++; } } newtFormAddComponent(form, newtEntry(col + 5, miscRow, "", 20, &miscParameters, NEWT_ENTRY_SCROLL)); newtOpenWindow((80 - (col + 30)) / 2, (25 - (miscRow + 6)) / 2, col + 30, miscRow + 6, "Module Parameters"); okay = newtButton((col + 10) / 3, miscRow + 2, "Ok"); cancel = newtButton(10 + 2 * ((col + 10) / 3), miscRow + 2, "Cancel"); newtFormAddComponents(form, okay, cancel, NULL); answer = newtRunForm(form); newtPopWindow(); if (answer == cancel) { newtFormDestroy(form); return INST_CANCEL; } if (mod) { i = *argcp; (*argcp) += numOptions; (*argvp) = realloc(*argvp, (*argcp + 1) * sizeof(char *)); numOptions = 0; option = mod->options; while (option->arg) { sprintf(buf, "%s=", option->arg); if (strcmp(parameters[numOptions], buf)) (*argvp)[i++] = strdup(parameters[numOptions]); numOptions++, option++; } (*argcp) = i; } chptr = miscParameters; numOptions = 0; while (*chptr) { while (isspace(*chptr) && *chptr) chptr++; if (!*chptr) continue; numOptions++; while (!isspace(*chptr) && *chptr) chptr++; } i = *argcp; (*argcp) += numOptions; (*argvp) = realloc(*argvp, (*argcp + 1) * sizeof(char *)); numOptions = 0; chptr = miscParameters; numOptions = 0; while (*chptr) { while (isspace(*chptr) && *chptr) chptr++; if (!*chptr) continue; start = chptr; numOptions++; while (!isspace(*chptr) && *chptr) chptr++; if (*chptr) { *chptr = '\0'; (*argvp)[i++] = strdup(start); *chptr = ' '; } else (*argvp)[i++] = strdup(start); } (*argcp) = i; (*argvp)[*argcp] = NULL; newtFormDestroy(form); return 0; } int readModuleConf(char * prefix, struct driversLoaded ** drlist) { char buf[255]; FILE * f; char * start, * end, * chptr; struct driversLoaded * item = NULL; if (testing) return 0; strcpy(buf, prefix); strcat(buf, "/conf.modules"); f = fopen(buf, "r"); if (!f) { return INST_ERROR; } while (fgets(buf, sizeof(buf) - 1, f)) { start = buf; end = start + strlen(start) - 1; *end = '\0'; if (!strncmp(start, "alias ", 6)) { start += 6; if (strncmp(start, "eth", 3)) continue; start += 5; item = allocDL(drlist); item->module = strdup(start); item->argv = NULL; item->argc = 0; item->type = DRIVER_ETHERNET; } else if (!strncmp(start, "options ", 8)) { start += 8; chptr = start; while (!isspace(*chptr) && *chptr) chptr++; if (!*chptr) continue; *chptr = '\0'; item = *drlist; while (item && strcmp(item->module, start)) item = item->next; if (!item) { item = allocDL(drlist); item->module = strdup(start); item->argv = NULL; item->argc = 0; item->type = DRIVER_ETHERNET; } item->argv = malloc(sizeof(char *) * 5); item->argc = 3; item->argv[2] = strdup(chptr + 1); } } fclose(f); return 0; } int writeModuleConf(char * prefix, struct driversLoaded * dl) { char buf[255]; FILE * f; int i; if (testing) return 0; strcpy(buf, prefix); strcat(buf, "/conf.modules"); f = fopen(buf, "w"); if (!f) { errorWindow("cannot create module config file: %s"); return INST_ERROR; } while (dl) { if (dl->type == DRIVER_ETHERNET) { fprintf(f, "alias eth0 %s\n", dl->module); } else if (dl->type == DRIVER_SCSI) { fprintf(f, "alias scsi_hostadapter %s\n", dl->module); } if (dl->argc > 2) { fprintf(f, "options %s", dl->module); for (i = 2; i < dl->argc; i++) { fprintf(f, " %s", dl->argv[i]); } fprintf(f, "\n"); } dl = dl->next; } fclose(f); return 0; } static int checkSCSIDev(struct driver * dev) { return scsiDeviceAvailable(); } static int checkEthernetDev(struct driver * dev) { return netDeviceAvailable("eth0"); } /* This assumes only one of each driver type is loaded */ int removeDeviceDriver(enum driverTypes type, struct driversLoaded ** drlist) { char * buf, * chptr; struct driversLoaded * dl, * head; struct driver * dri; dl = *drlist; while (dl && dl->type != type) { dl = dl->next; } if (!dl) return 0; dri = drivers; while (dri->name) { if (!strcmp(dri->modules, dl->module)) break; dri++; } if (!dri->name) return 0; buf = alloca(strlen(dri->modules) + 1); strcpy(buf, dri->modules); chptr = buf + strlen(buf) - 1; while (chptr > buf) { while (chptr > buf && *chptr != ':') chptr--; if (*chptr == ':') { chptr = '\0'; removeModule(chptr + 1); chptr--; } } removeModule(buf); if (dl == *drlist) { *drlist = dl->next; free(dl); } else if (dl) { head = *drlist; while (head->next != dl) head = head->next; head->next = dl->next; free(dl); } return 0; }