/** ** Copyright (c) 1995 ** Michael Smith, msmith@atrad.adelaide.edu.au. All rights reserved. ** ** This code contains a module marked : * Copyright (c) 1991 Regents of the University of California. * All rights reserved. * Copyright (c) 1994 Jordan K. Hubbard * All rights reserved. * Copyright (c) 1994 David Greenman * All rights reserved. * * Many additional changes by Bruce Evans * * This code is derived from software contributed by the * University of California Berkeley, Jordan K. Hubbard, * David Greenman and Bruce Evans. ** As such, it contains code subject to the above copyrights. ** The module and its copyright can be found below. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer as ** the first lines of this file unmodified. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. All advertising materials mentioning features or use of this software ** must display the following acknowledgment: ** This product includes software developed by Michael Smith. ** 4. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY MICHAEL SMITH ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL MICHAEL SMITH BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** ** $Id: userconfig.c,v 1.3 1996/08/31 15:06:33 asami Exp $ **/ /** ** USERCONFIG ** ** Kernel boot-time configuration manipulation tool for FreeBSD. ** ** Two modes of operation are supported : the default is the line-editor mode, ** the command "visual" invokes the fullscreen mode. ** ** The line-editor mode is the old favorite from FreeBSD 2.0/20.05 &c., the ** fullscreen mode requires syscons or a minimal-ansi serial console. **/ /** ** USERCONFIG, visual mode. ** ** msmith@atrad.adelaide.edu.au ** ** Look for "EDIT THIS LIST" to add to the list of known devices ** ** ** There are a number of assumptions made in this code. ** ** - That the console supports a minimal set of ANSI escape sequences ** (See the screen manipulation section for a summary) ** and has at least 24 rows. ** - That values less than or equal to zero for any of the device ** parameters indicate that the driver does not use the parameter. ** - That the only tunable parameter for PCI devices are their flags. ** - That flags are _always_ editable. ** ** Devices marked as disabled are imported as such. It is possible to move ** a PCI device onto the inactive list, but it is not possible to actually ** prevent the device from being probed. The ability to move is considered ** desirable in that people will complain otherwise 8) ** ** For this tool to be useful, the list of devices below _MUST_ be updated ** when a new driver is brought into the kernel. It is not possible to ** extract this information from the drivers in the kernel, as the devconf ** structure for the device is not registered until the device is probed, ** which is too late. ** ** XXX - TODO: ** ** - FIX OPERATION WITH PCVT! ** ** - Display _what_ a device conflicts with. ** - Implement page up/down (as what?) ** - Wizard mode (no restrictions) ** - Find out how to put syscons back into low-intensity mode so that the ** !b escape is useful on the console. ** - The min and max values used for editing parameters are probably ** very bogus - fix? ** ** - Only display headings with devices under them. (difficult) **/ /* * PC-9801 port by KATO Takenori */ #include #include #include #include #include #include #include #include #include static struct isa_device *devtabs[] = { isa_devtab_bio, isa_devtab_tty, isa_devtab_net, isa_devtab_null, NULL }; static struct isa_device *isa_devlist; /* list read by dset to extract changes */ #define putchar(x) cnputc(x) #define getchar() cngetc() #ifndef FALSE #define FALSE (0) #define TRUE (!FALSE) #endif typedef struct { char dev[16]; /* device basename */ char name[60]; /* long name */ int attrib; /* things to do with the device */ int class; /* device classification */ } DEV_INFO; #define FLG_INVISIBLE (1<<0) /* device should not be shown */ #define FLG_MANDATORY (1<<1) /* device can be edited but not disabled */ #define FLG_FIXIRQ (1<<2) /* device IRQ cannot be changed */ #define FLG_FIXIOBASE (1<<3) /* device iobase cannot be changed */ #define FLG_FIXMADDR (1<<4) /* device maddr cannot be changed */ #define FLG_FIXMSIZE (1<<5) /* device msize cannot be changed */ #define FLG_FIXDRQ (1<<6) /* device DRQ cannot be changed */ #define FLG_FIXED (FLG_FIXIRQ|FLG_FIXIOBASE|FLG_FIXMADDR|FLG_FIXMSIZE|FLG_FIXDRQ) #define FLG_IMMUTABLE (FLG_FIXED|FLG_MANDATORY) #define CLS_STORAGE 1 /* storage devices */ #define CLS_NETWORK 2 /* network interfaces */ #define CLS_COMMS 3 /* serial, parallel ports */ #define CLS_INPUT 4 /* user input : mice, keyboards, joysticks etc */ #define CLS_MMEDIA 5 /* "multimedia" devices (sound, video, etc) */ #define CLS_MISC 255 /* none of the above */ typedef struct { char name[60]; int number; } DEVCLASS_INFO; static DEVCLASS_INFO devclass_names[] = { { "Storage : ", CLS_STORAGE}, { "Network : ", CLS_NETWORK}, { "Communications : ", CLS_COMMS}, { "Input : ", CLS_INPUT}, { "Multimedia : ", CLS_MMEDIA}, { "Miscellaneous : ", CLS_MISC}, { "",0}}; /********************* EDIT THIS LIST **********************/ /** Notes : ** ** - PCI devices should be marked FLG_FIXED, not FLG_IMMUTABLE. Whilst ** it's impossible to disable them, it should be possible to move them ** from one list to another for peace of mind. ** - Devices that shouldn't be seen or removed should be marked FLG_INVISIBLE. ** - XXX The list below should be reviewed by the driver authors to verify ** that the correct flags have been set for each driver, and that the ** descriptions are accurate. **/ static DEV_INFO device_info[] = { /*---Name----- ---Description---------------------------------------------- */ #ifdef PC98 {"sbic", "PC-9801-55 SCSI Interface", 0, CLS_STORAGE}, {"bs", "PC-9801-55 SCSI Interface", 0, CLS_STORAGE}, {"aic", "Adaptec 152x SCSI and compatible sound cards", 0, CLS_STORAGE}, {"wdc", "IDE/ESDI/MFM disk controller", 0, CLS_STORAGE}, {"fdc", "Floppy disk controller", FLG_FIXED, CLS_STORAGE}, {"scd", "Sony CD-ROM", 0, CLS_STORAGE}, {"matcdc", "Matsushita/Panasonic/Creative CDROM", 0, CLS_STORAGE}, {"wt", "Wangtek/Archive QIC-02 Tape drive", 0, CLS_STORAGE}, {"ed", "NS8390 Ethernet adapters", 0, CLS_NETWORK}, {"ep", "3C509 Ethernet adapter", 0, CLS_NETWORK}, {"fe", "Fujitsu MD86960A/MB869685A Ethernet adapters", 0, CLS_NETWORK}, {"zp", "3COM PCMCIA Etherlink III Ethernet adapter", 0, CLS_NETWORK}, {"sio", "8250/16450/16550 Serial port", 0, CLS_COMMS}, {"lpt", "Parallel printer port", 0, CLS_COMMS}, {"mse", "PC-9801 Bus Mouse", 0, CLS_INPUT}, {"sc", "Syscons console driver", FLG_FIXED, CLS_INPUT}, {"pcm", "PC-9801-86 Sound Board", 0, CLS_MMEDIA}, {"mss", "Microsoft Sound System", 0, CLS_MMEDIA}, {"sb", "Soundblaster PCM (SB, SBPro, SB16, ProAudio Spectrum)",0,CLS_MMEDIA}, {"sbxvi", "Soundblaster 16", 0, CLS_MMEDIA}, {"sbmidi", "Soundblaster MIDI interface", 0, CLS_MMEDIA}, {"opl", "OPL-2/3 FM, Soundblaster, SBPro, SB16, ProAudio Spectrum",0,CLS_MMEDIA}, {"mpu", "Roland MPU401 MIDI", 0, CLS_MMEDIA}, {"pca", "PC speaker PCM audio driver", FLG_FIXED, CLS_MMEDIA}, {"apm", "Advanced Power Management", FLG_FIXED, CLS_MISC}, {"npx", "Math coprocessor", FLG_INVISIBLE, CLS_MISC}, {"lkm", "Loadable PCI driver support", FLG_INVISIBLE, CLS_MISC}, {"vga", "Catchall PCI VGA driver", FLG_INVISIBLE, CLS_MISC}, {"chip", "PCI chipset support", FLG_INVISIBLE, CLS_MISC}, #else {"bt", "Buslogic SCSI controller", 0, CLS_STORAGE}, {"ahc", "Adaptec 274x/284x/294x SCSI controller", 0, CLS_STORAGE}, {"ahb", "Adaptec 174x SCSI controller", 0, CLS_STORAGE}, {"aha", "Adaptec 154x SCSI controller", 0, CLS_STORAGE}, {"uha", "Ultrastor 14F/24F/34F SCSI controller",0, CLS_STORAGE}, {"aic", "Adaptec 152x SCSI and compatible sound cards", 0, CLS_STORAGE}, {"nca", "ProAudio Spectrum SCSI and comaptibles", 0, CLS_STORAGE}, {"sea", "Seagate ST01/ST02 SCSI and compatibles", 0, CLS_STORAGE}, {"wds", "Western Digitial WD7000 SCSI controller", 0, CLS_STORAGE}, {"ncr", "NCR 53C810 SCSI controller", FLG_FIXED, CLS_STORAGE}, {"wdc", "IDE/ESDI/MFM disk controller", 0, CLS_STORAGE}, {"fdc", "Floppy disk controller", FLG_FIXED, CLS_STORAGE}, {"mcd", "Mitsumi CD-ROM", 0, CLS_STORAGE}, {"scd", "Sony CD-ROM", 0, CLS_STORAGE}, {"matcdc", "Matsushita/Panasonic/Creative CDROM", 0, CLS_STORAGE}, {"wt", "Wangtek/Archive QIC-02 Tape drive", 0, CLS_STORAGE}, {"ed", "NE1000,NE2000,3C503,WD/SMC80xx Ethernet adapters",0, CLS_NETWORK}, {"el", "3C501 Ethernet adapter", 0, CLS_NETWORK}, {"ep", "3C509 Ethernet adapter", 0, CLS_NETWORK}, {"fe", "Fujitsu MD86960A/MB869685A Ethernet adapters", 0, CLS_NETWORK}, {"fea", "DEC DEFEA EISA FDDI adapter", 0, CLS_NETWORK}, {"ie", "AT&T Starlan 10 and EN100, 3C507, NI5210 Ethernet adapters",0,CLS_NETWORK}, {"ix", "Intel EtherExpress Ethernet adapter", 0, CLS_NETWORK}, {"le", "DEC Etherworks 2 and 3 Ethernet adapters", 0, CLS_NETWORK}, {"lnc", "Isolan, Novell NE2100/NE32-VL Ethernet adapters", 0,CLS_NETWORK}, {"ze", "IBM/National Semiconductor PCMCIA Ethernet adapter",0, CLS_NETWORK}, {"zp", "3COM PCMCIA Etherlink III Ethernet adapter", 0, CLS_NETWORK}, {"de", "DEC DC21040 Ethernet adapter", FLG_FIXED, CLS_NETWORK}, {"fpa", "DEC DEFPA PCI FDDI adapter", FLG_FIXED, CLS_NETWORK}, {"sio", "8250/16450/16550 Serial port", 0, CLS_COMMS}, {"cx", "Cronyx/Sigma multiport sync/async adapter",0, CLS_COMMS}, {"rc", "RISCom/8 multiport async adapter", 0, CLS_COMMS}, {"cy", "Cyclades multiport async adapter", 0, CLS_COMMS}, {"lpt", "Parallel printer port", 0, CLS_COMMS}, {"nic", "ISDN driver", 0, CLS_COMMS}, {"nnic", "ISDN driver", 0, CLS_COMMS}, {"gp", "National Instruments AT-GPIB/TNT driver", 0, CLS_COMMS}, {"mse", "Microsoft Bus Mouse", 0, CLS_INPUT}, {"psm", "PS/2 Mouse", 0, CLS_INPUT}, {"joy", "Joystick", FLG_FIXED, CLS_INPUT}, {"vt", "PCVT console driver", FLG_FIXED, CLS_INPUT}, {"sc", "Syscons console driver", FLG_FIXED, CLS_INPUT}, {"sb", "Soundblaster PCM (SB, SBPro, SB16, ProAudio Spectrum)",0,CLS_MMEDIA}, {"sbxvi", "Soundblaster 16", 0, CLS_MMEDIA}, {"sbmidi", "Soundblaster MIDI interface", 0, CLS_MMEDIA}, {"pas", "ProAudio Spectrum PCM and MIDI", 0, CLS_MMEDIA}, {"gus", "Gravis Ultrasound, Ultrasound 16 and Ultrasound MAX",0,CLS_MMEDIA}, {"gusxvi", "Gravis Ultrasound 16-bit PCM", 0, CLS_MMEDIA}, {"gusmax", "Gravis Ultrasound MAX", 0, CLS_MMEDIA}, {"mss", "Microsoft Sound System", 0, CLS_MMEDIA}, {"opl", "OPL-2/3 FM, Soundblaster, SBPro, SB16, ProAudio Spectrum",0,CLS_MMEDIA}, {"mpu", "Roland MPU401 MIDI", 0, CLS_MMEDIA}, {"uart", "6850 MIDI UART", 0, CLS_MMEDIA}, {"pca", "PC speaker PCM audio driver", FLG_FIXED, CLS_MMEDIA}, {"ctx", "Coretex-I frame grabber", 0, CLS_MMEDIA}, {"spigot", "Creative Labs Video Spigot video capture", 0, CLS_MMEDIA}, {"gsc", "Genius GS-4500 hand scanner", 0, CLS_MMEDIA}, {"qcam", "QuickCam parallel port camera", 0, CLS_MMEDIA}, {"apm", "Advanced Power Management", FLG_FIXED, CLS_MISC}, {"labpc", "National Instruments Lab-PC/Lab-PC+", 0, CLS_MISC}, {"npx", "Math coprocessor", FLG_INVISIBLE, CLS_MISC}, {"lkm", "Loadable PCI driver support", FLG_INVISIBLE, CLS_MISC}, {"vga", "Catchall PCI VGA driver", FLG_INVISIBLE, CLS_MISC}, {"chip", "PCI chipset support", FLG_INVISIBLE, CLS_MISC}, #endif {"","",0,0}}; typedef struct _devlist_struct { char name[80]; int attrib; /* flag values as per the FLG_* defines above */ int class; /* disk, etc as per the CLS_* defines above */ char dev[16]; int iobase,irq,drq,maddr,msize,unit,flags,conflict_ok,id; int comment; /* 0 = device, 1 = comment, 2 = collapsed comment */ int conflicts; /* set/reset by findconflict, count of conflicts */ int changed; /* nonzero if the device has been edited */ struct isa_device *device; struct _devlist_struct *prev,*next; } DEV_LIST; #define DEV_DEVICE 0 #define DEV_COMMENT 1 #define DEV_ZOOMED 2 #define LIST_CURRENT (1<<0) #define LIST_SELECTED (1<<1) #define KEY_EXIT 0 /* return codes from dolist() and friends */ #define KEY_DO 1 #define KEY_DEL 2 #define KEY_TAB 3 #define KEY_REDRAW 4 #define KEY_UP 5 /* these only returned from editval() */ #define KEY_DOWN 6 #define KEY_LEFT 7 #define KEY_RIGHT 8 #define KEY_NULL 9 /* this allows us to spin & redraw */ #define KEY_ZOOM 10 /* these for zoom all/collapse all */ #define KEY_UNZOOM 11 static void redraw(void); static void insdev(DEV_LIST *dev, DEV_LIST *list); static int devinfo(DEV_LIST *dev); static int visuserconfig(void); static DEV_LIST *active = NULL,*inactive = NULL; /* driver lists */ static DEV_LIST *alist,*ilist; /* visible heads of the driver lists */ static DEV_LIST scratch; /* scratch record */ static int conflicts; /* total conflict count */ static char lines[] = "--------------------------------------------------------------------------------"; static char spaces[] = " "; /** ** Device manipulation stuff : find, describe, configure. **/ /** ** setdev ** ** Sets the device referenced by (*dev) to the parameters in the struct, ** and the enable flag according to (enabled) **/ static void setdev(DEV_LIST *dev, int enabled) { if (!dev->device) /* PCI device */ return; dev->device->id_iobase = dev->iobase; /* copy happy */ dev->device->id_irq = (u_short)(dev->irq < 16 ? 1<irq : 0); /* IRQ is bitfield */ dev->device->id_drq = (short)dev->drq; dev->device->id_maddr = (caddr_t)dev->maddr; dev->device->id_msize = dev->msize; dev->device->id_flags = dev->flags; dev->device->id_enabled = enabled; } /** ** getdevs ** ** Walk the kernel device tables and build the active and inactive lists **/ static void getdevs(void) { int i,j; struct isa_device *ap; for (j = 0; devtabs[j]; j++) /* ISA devices */ { ap = devtabs[j]; /* pointer to array of devices */ for (i = 0; ap[i].id_id; i++) /* for each device in this table */ { scratch.unit = ap[i].id_unit; /* device parameters */ strcpy(scratch.dev,ap[i].id_driver->name); scratch.iobase = ap[i].id_iobase; scratch.irq = ffs(ap[i].id_irq)-1; scratch.drq = ap[i].id_drq; scratch.maddr = (int)ap[i].id_maddr; scratch.msize = ap[i].id_msize; scratch.flags = ap[i].id_flags; scratch.conflict_ok = ap[i].id_conflicts; scratch.comment = DEV_DEVICE; /* admin stuff */ scratch.conflicts = 0; scratch.device = &ap[i]; /* save pointer for later reference */ scratch.changed = 0; if (!devinfo(&scratch)) /* get more info on the device */ insdev(&scratch,ap[i].id_enabled?active:inactive); } } #if NPCI > 0 for (i = 0; i < pcidevice_set.ls_length; i++) { if (pcidevice_set.ls_items[i]) { if (((struct pci_device *)pcidevice_set.ls_items[i])->pd_name) { strcpy(scratch.dev,((struct pci_device *)pcidevice_set.ls_items[i])->pd_name); scratch.iobase = -2; /* mark as PCI for future reference */ scratch.irq = -2; scratch.drq = -2; scratch.maddr = -2; scratch.msize = -2; scratch.flags = 0; scratch.conflict_ok = 0; /* shouldn't conflict */ scratch.comment = DEV_DEVICE; /* is a device */ scratch.unit = 0; /* arbitrary number of them */ scratch.conflicts = 0; scratch.device = NULL; scratch.changed = 0; if (!devinfo(&scratch)) insdev(&scratch,active); /* always active */ } } } #endif /* NPCI > 0 */ } /** ** Devinfo ** ** Fill in (dev->name), (dev->attrib) and (dev->type) from the device_info array. ** If the device is unknown, put it in the CLS_MISC class, with no flags. ** ** If the device is marked "invisible", return nonzero; the caller should ** not insert any such device into either list. **/ static int devinfo(DEV_LIST *dev) { int i; for (i = 0; device_info[i].class; i++) { if (!strcmp(dev->dev,device_info[i].dev)) { if (device_info[i].attrib & FLG_INVISIBLE) return(1); strcpy(dev->name,device_info[i].name); dev->attrib = device_info[i].attrib; dev->class = device_info[i].class; return(0); } } strcpy(dev->name,"Unknown device"); dev->attrib = 0; dev->class = CLS_MISC; return(0); } /** ** List manipulation stuff : add, move, initialise, free, traverse ** ** Note that there are assumptions throughout this code that ** the first entry in a list will never move. (assumed to be ** a comment). **/ /** ** Adddev ** ** appends a copy of (dev) to the end of (*list) **/ static void addev(DEV_LIST *dev, DEV_LIST **list) { DEV_LIST *lp,*ap; lp = (DEV_LIST *)malloc(sizeof(DEV_LIST),M_DEVL,M_WAITOK); bcopy(dev,lp,sizeof(DEV_LIST)); /* create copied record */ if (*list) /* list exists */ { ap = *list; while(ap->next) ap = ap->next; /* scoot to end of list */ lp->prev = ap; lp->next = NULL; ap->next = lp; }else{ /* list does not yet exist */ *list = lp; lp->prev = lp->next = NULL; /* list now exists */ } } /** ** Findspot ** ** Finds the 'appropriate' place for (dev) in (list) ** ** 'Appropriate' means in numeric order with other devices of the same type, ** or in alphabetic order following a comment of the appropriate type. ** or at the end of the list if an appropriate comment is not found. (this should ** never happen) ** (Note that the appropriate point is never the top, but may be the bottom) **/ static DEV_LIST * findspot(DEV_LIST *dev, DEV_LIST *list) { DEV_LIST *ap; for (ap = list; ap; ap = ap->next) { if (ap->comment != DEV_DEVICE) /* ignore comments */ continue; if (!strcmp(dev->dev,ap->dev)) /* same base device */ { if ((dev->unit <= ap->unit) /* belongs before (equal is bad) */ || !ap->next) /* or end of list */ { ap = ap->prev; /* back up one */ break; /* done here */ } if (ap->next) /* if the next item exists */ { if (ap->next->comment != DEV_DEVICE) /* next is a comment */ break; if (strcmp(dev->dev,ap->next->dev)) /* next is a different device */ break; } } } if (!ap) { for (ap = list; ap; ap = ap->next) { if (ap->comment != DEV_DEVICE) /* look for simlar devices */ continue; if (dev->class != ap->class) /* of same class too 8) */ continue; if (strcmp(dev->dev,ap->dev) < 0) /* belongs before the current entry */ { ap = ap->prev; /* back up one */ break; /* done here */ } if (ap->next) /* if the next item exists */ if (ap->next->comment != DEV_DEVICE) /* next is a comment, go here */ break; } } if (!ap) /* didn't find a match */ { for (ap = list; ap->next; ap = ap->next) /* try for a matching comment */ if ((ap->comment != DEV_DEVICE) && (ap->class == dev->class)) /* appropriate place? */ break; } /* or just put up with last */ return(ap); } /** ** Insdev ** ** Inserts a copy of (dev) at the appropriate point in (list) **/ static void insdev(DEV_LIST *dev, DEV_LIST *list) { DEV_LIST *lp,*ap; lp = (DEV_LIST *)malloc(sizeof(DEV_LIST),M_DEVL,M_WAITOK); bcopy(dev,lp,sizeof(DEV_LIST)); /* create copied record */ ap = findspot(lp,list); /* find appropriate spot */ lp->next = ap->next; /* point to next */ if (ap->next) ap->next->prev = lp; /* point next to new */ lp->prev = ap; /* point new to current */ ap->next = lp; /* and current to new */ } /** ** Movedev ** ** Moves (dev) from its current list to an appropriate place in (list) ** (dev) may not come from the top of a list, but it may from the bottom. **/ static void movedev(DEV_LIST *dev, DEV_LIST *list) { DEV_LIST *ap; ap = findspot(dev,list); dev->prev->next = dev->next; /* remove from old list */ if (dev->next) dev->next->prev = dev->prev; dev->next = ap->next; /* insert in new list */ if (ap->next) ap->next->prev = dev; /* point next to new */ dev->prev = ap; /* point new to current */ ap->next = dev; /* and current to new */ } /** ** Initlist ** ** Initialises (*list) with the basic headings **/ static void initlist(DEV_LIST **list) { int i; for(i = 0; devclass_names[i].name[0]; i++) /* for each devtype name */ { strcpy(scratch.name,devclass_names[i].name); scratch.comment = DEV_ZOOMED; scratch.class = devclass_names[i].number; scratch.attrib = FLG_MANDATORY; /* can't be moved */ addev(&scratch,list); /* add to the list */ } } /** ** savelist ** ** Walks (list) and saves the settings of any entry marked as changed. ** ** The device's active field is set according to (active). ** ** Builds the isa_devlist used by dset to extract the changed device information. ** The code for this was taken almost verbatim from the original module. **/ static void savelist(DEV_LIST *list, int active) { struct isa_device *id_p,*id_pn; while (list) { if ((list->comment == DEV_DEVICE) && list->changed) { setdev(list,active); /* set the device itself */ id_pn = NULL; for (id_p=isa_devlist; id_p; id_p=id_p->id_next) { /* look on the list for it */ if (id_p->id_id == list->device->id_id) { id_pn = id_p->id_next; bcopy(list->device,id_p,sizeof(struct isa_device)); id_p->id_next = id_pn; break; } } if (!id_pn) /* not already on the list */ { id_pn = malloc(sizeof(struct isa_device),M_DEVL,M_WAITOK); bcopy(list->device,id_pn,sizeof(struct isa_device)); id_pn->id_next = isa_devlist; isa_devlist = id_pn; /* park at top of list */ } } list = list->next; } } /** ** nukelist ** ** Frees all storage in use by a (list). **/ static void nukelist(DEV_LIST *list) { DEV_LIST *dp; if (!list) return; while(list->prev) /* walk to head of list */ list = list->prev; while(list) { dp = list; list = list->next; free(dp,M_DEVL); } } /** ** prevent ** ** Returns the previous entry in (list), skipping zoomed regions. Returns NULL ** if there is no previous entry. (Only possible if list->prev == NULL given the ** premise that there is always a comment at the head of the list) **/ static DEV_LIST * prevent(DEV_LIST *list) { DEV_LIST *dp; if (!list) return(NULL); dp = list->prev; /* start back one */ while(dp) { if (dp->comment == DEV_ZOOMED) /* previous section is zoomed */ return(dp); /* so skip to comment */ if (dp->comment == DEV_COMMENT) /* not zoomed */ return(list->prev); /* one back as normal */ dp = dp->prev; /* backpedal */ } return(dp); /* NULL, we can assume */ } /** ** nextent ** ** Returns the next entry in (list), skipping zoomed regions. Returns NULL ** if there is no next entry. (Possible if the current entry is last, or ** if the current entry is the last heading and it's collapsed) **/ static DEV_LIST * nextent(DEV_LIST *list) { DEV_LIST *dp; if (!list) return(NULL); if (list->comment != DEV_ZOOMED) /* no reason to skip */ return(list->next); dp = list->next; while(dp) { if (dp->comment != DEV_DEVICE) /* found another heading */ break; dp = dp->next; } return(dp); /* back we go */ } /** ** ofsent ** ** Returns the (ofs)th entry down from (list), or NULL if it doesn't exist **/ static DEV_LIST * ofsent(int ofs, DEV_LIST *list) { while (ofs-- && list) list = nextent(list); return(list); } /** ** findconflict ** ** Scans every element of (list) and sets the conflict tags appropriately ** Returns the number of conflicts found. **/ static int findconflict(DEV_LIST *list) { int count = 0; /* number of conflicts found */ DEV_LIST *dp,*sp; for (dp = list; dp; dp = dp->next) /* over the whole list */ { if (dp->comment != DEV_DEVICE) /* comments don't usually conflict */ continue; dp->conflicts = 0; /* assume the best */ for (sp = list; sp; sp = sp->next) /* scan the entire list for conflicts */ { if (sp->comment != DEV_DEVICE) /* likewise */ continue; if (sp == dp) /* always conflict with itself */ continue; if (sp->conflict_ok && dp->conflict_ok) continue; /* both allowed to conflict */ if ((dp->iobase > 0) && /* iobase conflict? */ (dp->iobase == sp->iobase)) dp->conflicts = 1; if ((dp->irq > 0) && /* irq conflict? */ (dp->irq == sp->irq)) dp->conflicts = 1; if ((dp->drq > 0) && /* drq conflict? */ (dp->drq == sp->drq)) dp->conflicts = 1; if ((dp->maddr > 0) && /* maddr conflict? */ (dp->maddr == sp->maddr)) dp->conflicts = 1; if ((dp->msize > 0) && /* msize conflict? */ (dp->msize == sp->msize)) dp->conflicts = 1; } count += dp->conflicts; /* count conflicts */ } return(count); } /** ** expandlist ** ** Unzooms all headings in (list) **/ static void expandlist(DEV_LIST *list) { while(list) { if (list->comment == DEV_COMMENT) list->comment = DEV_ZOOMED; list = list->next; } } /** ** collapselist ** ** Zooms all headings in (list) **/ static void collapselist(DEV_LIST *list) { while(list) { if (list->comment == DEV_ZOOMED) list->comment = DEV_COMMENT; list = list->next; } } /** ** Screen-manipulation stuff ** ** This is the basic screen layout : ** ** 0 5 10 15 20 25 30 35 40 45 50 55 60 67 70 75 ** |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|.... ** +--------------------------------------------------------------------------------+ ** 0 -|---Active Drivers----------------------------xx Conflicts------Dev---IRQ--Port--| ** 1 -| ........................ ....... .. 0x....| ** 2 -| ........................ ....... .. 0x....| ** 3 -| ........................ ....... .. 0x....| ** 4 -| ........................ ....... .. 0x....| ** 5 -| ........................ ....... .. 0x....| ** 6 -| ........................ ....... .. 0x....| ** 7 -| ........................ ....... .. 0x....| ** 8 -| ........................ ....... .. 0x....| ** 9 -|---Inactive Drivers--------------------------------------------Dev--------------| ** 10-| ........................ ....... | ** 11-| ........................ ....... | ** 12-| ........................ ....... | ** 13-| ........................ ....... | ** 14-| ........................ ....... | ** 15-| ........................ ....... | ** 16-| ........................ ....... | ** 17-|------------------------------------------------------UP-DOWN-------------------| ** 18-| Relevant parameters for the current device | ** 19-| | ** 20-| | ** 21-|--------------------------------------------------------------------------------| ** 22-| Help texts go here | ** 23-| | ** +--------------------------------------------------------------------------------+ ** ** Help texts ** ** On a collapsed comment : ** ** [Enter] Expand device list [z] Expand all lists ** [TAB] Change fields [Q] Save and Exit ** ** On an expanded comment : ** ** [Enter] Collapse device list [Z] Collapse all lists ** [TAB] Change fields [Q] Save and Exit ** ** On a comment with no followers ** ** ** [TAB] Change fields [Q] Save and Exit ** ** On a device in the active list ** ** [Enter] Edit device parameters [DEL] Disable device ** [TAB] Change fields [Q] Save and Exit ** ** On a device in the inactive list ** ** [Enter] Enable device ** [TAB] Change fields [Q] Save and Exit ** ** While editing parameters ** ** ** [TAB] Change fields [Q] Save device parameters **/ /** ** ** The base-level screen primitives : ** ** bold() - enter bold mode \E[1m (md) ** inverse() - enter inverse mode \E[7m (so) ** normal() - clear bold/inverse mode \E[m (se) ** clear() - clear the screen \E[H\E[J (ce) ** move(x,y) - move the cursor to x,y \E[y;xH: (cm) **/ static void bold(void) { printf("\033[1m"); } static void inverse(void) { printf("\033[7m"); } static void normal(void) { printf("\033[m"); } static void clear(void) { normal(); printf("\033[H\033[J"); } static void move(int x, int y) { printf("\033[%d;%dH",y+1,x+1); } /** ** ** High-level screen primitives : ** ** putxyl(x,y,str,len) - put (len) bytes of (str) at (x,y), supports embedded formatting ** putxy(x,y,str) - put (str) at (x,y), supports embedded formatting ** erase(x,y,w,h) - clear the box (x,y,w,h) ** txtbox(x,y,w,y,str) - put (str) in a region at (x,y,w,h) ** putmsg(str) - put (str) in the message area ** puthelp(str) - put (str) in the upper helpline ** pad(str,len) - pad (str) to (len) with spaces ** drawline(row,detail,list,inverse,*dhelp) ** - draws a line for (*list) at (row) onscreen. If (detail) is ** nonzero, include port, IRQ and maddr, if (inverse) is nonzero, ** draw the line in inverse video, and display (*dhelp) on the ** helpline. ** drawlist(row,num,detail,list) ** - draw (num) entries from (list) at (row) onscreen, passile (detail) ** through to drawline(). ** showparams(dev) - displays the relevant parameters for (dev) below the lists onscreen. ** yesno(str) - displays (str) in the message area, and returns nonzero on 'y' or 'Y' ** redraw(); - Redraws the entire screen layout, including the ** - two list panels. **/ /** ** putxy ** writes (str) at x,y onscreen ** putxyl ** writes up to (len) of (str) at x,y onscreen. ** ** Supports embedded formatting : ** !i - inverse mode. ** !b - bold mode. ** !n - normal mode. **/ static void putxyl(int x, int y, char *str, int len) { move(x,y); normal(); while((*str) && (len--)) { if (*str == '!') /* format escape? */ { switch(*(str+1)) /* depending on the next character */ { case 'i': inverse(); str +=2; /* skip formatting */ len++; /* doesn't count for length */ break; case 'b': bold(); str +=2; /* skip formatting */ len++; /* doesn't count for length */ break; case 'n': normal(); str +=2; /* skip formatting */ len++; /* doesn't count for length */ break; default: putchar(*str++); /* not an escape */ } }else{ putchar(*str++); /* emit the character */ } } } #define putxy(x,y,str) putxyl(x,y,str,-1) /** ** erase ** ** Erases the region (x,y,w,h) **/ static void erase(int x, int y, int w, int h) { int i; normal(); for (i = 0; i < h; i++) putxyl(x,y++,spaces,w); } /** ** txtbox ** ** Writes (str) into the region (x,y,w,h), supports embedded formatting using ** putxy. Lines are not wrapped, newlines must be forced with \n. **/ static void txtbox(int x, int y, int w, int h, char *str) { int i = 0; h--; while((str[i]) && h) { if (str[i] == '\n') /* newline */ { putxyl(x,y,str,(i= len) /* no padding needed */ return; while(i < len) /* pad */ str[i++] = ' '; str[i] = '\0'; } /** ** drawline ** ** Displays entry (ofs) of (list) in region at (row) onscreen, optionally displaying ** the port and IRQ fields if (detail) is nonzero. If (inverse), in inverse video. ** ** The text (dhelp) is displayed if the item is a normal device, otherwise ** help is shown for normal or zoomed comments **/ static void drawline(int row, int detail, DEV_LIST *list, int inverse, char *dhelp) { char lbuf[90],nb[70],db[20],ib[16],pb[16]; if (list->comment == DEV_DEVICE) { nb[0] = ' '; strncpy(nb+1,list->name,57); }else{ strncpy(nb,list->name,58); if ((list->comment == DEV_ZOOMED) && (list->next)) if (list->next->comment == DEV_DEVICE) /* only mention if there's something hidden */ strcat(nb," (Collapsed)"); } nb[58] = '\0'; pad(nb,60); if (list->conflicts) /* device in conflict? */ if (inverse) { strcpy(nb+54," !nCONF!i "); /* tag conflict, careful of length */ }else{ strcpy(nb+54," !iCONF!n "); /* tag conflict, careful of length */ } if (list->comment == DEV_DEVICE) { sprintf(db,"%s%d",list->dev,list->unit); pad(db,8); }else{ strcpy(db," "); } if ((list->irq > 0) && detail && (list->comment == DEV_DEVICE)) { sprintf(ib," %d",list->irq); pad(ib,4); }else{ strcpy(ib," "); } if ((list->iobase > 0) && detail && (list->comment == DEV_DEVICE)) { sprintf(pb,"0x%x",list->iobase); pad(pb,7); }else{ strcpy(pb," "); } sprintf(lbuf," %s%s%s%s%s",inverse?"!i":"",nb,db,ib,pb); putxyl(0,row,lbuf,80); if (dhelp) { switch(list->comment) { case DEV_DEVICE: /* ordinary device */ puthelp(dhelp); break; case DEV_COMMENT: puthelp(""); if (list->next) if (list->next->comment == DEV_DEVICE) puthelp(" [!bEnter!n] Collapse device list [!bC!n] Collapse all lists"); break; case DEV_ZOOMED: puthelp(""); if (list->next) if (list->next->comment == DEV_DEVICE) puthelp(" [!bEnter!n] Expand device list [!bX!n] Expand all lists"); break; default: puthelp(" WARNING: This list entry corrupted!"); break; } } move(0,row); /* put the cursor somewhere relevant */ } /** ** drawlist ** ** Displays (num) lines of the contents of (list) at (row), optionally displaying the ** port and IRQ fields as well if (detail) is nonzero ** ** printf in the kernel is essentially useless, so we do most of the hard work ourselves here. **/ static void drawlist(int row, int num, int detail, DEV_LIST *list) { int ofs; for(ofs = 0; ofs < num; ofs++) { if (list) { drawline(row+ofs,detail,list,0,""); list = nextent(list); /* move down visible list */ }else{ erase(0,row+ofs,80,1); } } } /** ** redrawactive ** ** Redraws the active list **/ static void redrawactive(void) { char cbuf[16]; if (conflicts) { sprintf(cbuf,"!i%d conflict%s",conflicts,(conflicts>1)?"s":""); putxy(45,0,cbuf); }else{ putxyl(45,0,lines,16); } drawlist(1,8,1,alist); /* draw device lists */ } /** ** redrawinactive ** ** Redraws the inactive list **/ static void redrawinactive(void) { drawlist(10,7,0,ilist); /* draw device lists */ } /** ** redraw ** ** Clear the screen and redraw the entire layout **/ static void redraw(void) { clear(); putxy(0,0,lines); putxy(3,0,"!bActive!n-!bDrivers"); putxy(63,0,"!bDev!n---!bIRQ!n--!bPort"); putxy(0,9,lines); putxy(3,9,"!bInactive!n-!bDrivers"); putxy(63,9,"!bDev"); putxy(0,17,lines); putxy(0,21,lines); masterhelp(" [!bTAB!n] Change fields [!bQ!n] Save and Exit"); redrawactive(); redrawinactive(); } /** ** yesnocancel ** ** Put (str) in the message area, and return 1 if the user hits 'y' or 'Y', ** 2 if they hit 'c' or 'C', or 0 for 'n' or 'N'. **/ static int yesnocancel(char *str) { putmsg(str); for(;;) switch(getchar()) { case 'n': case 'N': return(0); case 'y': case 'Y': return(1); case 'c': case 'C': return(2); } } /** ** showparams ** ** Show device parameters in the region below the lists ** ** 0 5 10 15 20 25 30 35 40 45 50 55 60 67 70 75 ** |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|.... ** +--------------------------------------------------------------------------------+ ** 17-|--------------------------------------------------------------------------------| ** 18-| Port address : 0x0000 Memory address : 0x00000 Conflict allowed | ** 19-| IRQ number : 00 Memory size : 0x0000 | ** 20-| Flags : 0x0000 DRQ number : 00 | ** 21-|--------------------------------------------------------------------------------| **/ static void showparams(DEV_LIST *dev) { char buf[80]; erase(0,18,80,3); /* clear area */ if (!dev) return; if (dev->comment != DEV_DEVICE) return; if (dev->iobase > 0) { sprintf(buf,"Port address : 0x%x",dev->iobase); putxy(1,18,buf); } if (dev->irq > 0) { sprintf(buf,"IRQ number : %d",dev->irq); putxy(1,19,buf); } sprintf(buf,"Flags : 0x%x",dev->flags); putxy(1,20,buf); if (dev->maddr > 0) { sprintf(buf,"Memory address : 0x%x",dev->maddr); putxy(26,18,buf); } if (dev->msize > 0) { sprintf(buf,"Memory size : 0x%x",dev->msize); putxy(26,19,buf); } if (dev->drq > 0) { sprintf(buf,"DRQ number : %d",dev->drq); putxy(26,20,buf); } if (dev->conflict_ok) putxy(54,18,"Conflict allowed"); } /** ** Editing functions for device parameters ** ** editval(x,y,width,hex,min,max,val) - Edit (*val) in a field (width) wide at (x,y) ** onscreen. Refuse values outsise (min) and (max). ** editparams(dev) - Edit the parameters for (dev) **/ #define VetRet(code) \ { \ if ((i >= min) && (i <= max)) /* legit? */ \ { \ *val = i; \ sprintf(buf,hex?"0x%x":"%d",i); \ putxy(hex?x-2:x,y,buf); \ return(code); /* all done and exit */ \ } \ i = *val; /* restore original value */ \ delta = 1; /* restore other stuff */ \ } /** ** editval ** ** Edit (*val) at (x,y) in (hex)?hex:decimal mode, allowing values between (min) and (max) ** in a field (width) wide. (Allow one space) ** If (ro) is set, we're in "readonly" mode, so disallow edits. ** ** Return KEY_TAB on \t, KEY_EXIT on 'q' **/ static int editval(int x, int y, int width, int hex, int min, int max, int *val, int ro) { int i = *val; /* work with copy of the value */ char buf[10],tc[8]; /* display buffer, text copy */ int xp = 0; /* cursor offset into text copy */ int delta = 1; /* force redraw first time in */ int c; int extended = 0; /* stage counter for extended key sequences */ if (hex) /* we presume there's a leading 0x onscreen */ putxy(x-2,y,"!i0x"); /* coz there sure is now */ for (;;) { if (delta) /* only update if necessary */ { sprintf(tc,hex?"%x":"%d",i); /* make a text copy of the value */ sprintf(buf,"!i%s",tc); /* format for printing */ erase(x,y,width,1); /* clear the area */ putxy(x,y,buf); /* write */ xp = strlen(tc); /* cursor always at end */ move(x+xp,y); /* position the cursor */ } c = getchar(); switch(extended) /* escape handling */ { case 0: if (c == 0x1b) /* esc? */ { extended = 1; /* flag and spin */ continue; } extended = 0; break; /* nope, drop through */ case 1: /* there was an escape prefix */ if (c == '[' || c == 'O') /* second character in sequence */ { extended = 2; continue; } if (c == 0x1b) return(KEY_EXIT); /* double esc exits */ extended = 0; break; /* nup, not a sequence. */ case 2: extended = 0; switch(c) /* looks like the real McCoy */ { case 'A': VetRet(KEY_UP); /* leave if OK */ continue; case 'B': VetRet(KEY_DOWN); /* leave if OK */ continue; case 'C': VetRet(KEY_RIGHT); /* leave if OK */ continue; case 'D': VetRet(KEY_LEFT); /* leave if OK */ continue; default: continue; } } switch(c) { case '\t': /* trying to tab off */ VetRet(KEY_TAB); /* verify and maybe return */ break; case 'q': case 'Q': VetRet(KEY_EXIT); break; case '\b': case '\177': /* BS or DEL */ if (ro) /* readonly? */ { puthelp(" !iThis value cannot be edited (Press ESC)"); while(getchar() != 0x1b); /* wait for key */ return(KEY_NULL); /* spin */ } if (xp) /* still something left to delete */ { i = i / (hex?0x10:10); /* strip last digit */ delta = 1; /* force update */ } break; case 588: VetRet(KEY_UP); break; case 596: VetRet(KEY_DOWN); break; case 591: VetRet(KEY_LEFT); break; case 593: VetRet(KEY_RIGHT); break; default: if (ro) /* readonly? */ { puthelp(" !iThis value cannot be edited (Press ESC)"); while(getchar() != 0x1b); /* wait for key */ return(KEY_NULL); /* spin */ } if (xp >= width) /* no room for more characters anyway */ break; if (hex) { if ((c >= '0') && (c <= '9')) { i = i*0x10 + (c-'0'); /* update value */ delta = 1; break; } if ((c >= 'a') && (c <= 'f')) { i = i*0x10 + (c-'a'+0xa); delta = 1; break; } if ((c >= 'A') && (c <= 'F')) { i = i*0x10 + (c-'A'+0xa); delta = 1; break; } }else{ if ((c >= '0') && (c <= '9')) { i = i*10 + (c-'0'); /* update value */ delta = 1; /* force redraw */ break; } } break; } } } /** ** editparams ** ** Edit the parameters for (dev) ** ** Note that it's _always_ possible to edit the flags, otherwise it might be ** possible for this to spin in an endless loop... ** 0 5 10 15 20 25 30 35 40 45 50 55 60 67 70 75 ** |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|.... ** +--------------------------------------------------------------------------------+ ** 17-|--------------------------------------------------------------------------------| ** 18-| Port address : 0x0000 Memory address : 0x00000 Conflict allowed | ** 19-| IRQ number : 00 Memory size : 0x0000 | ** 20-| Flags : 0x0000 DRQ number : 00 | ** 21-|--------------------------------------------------------------------------------| ** ** The "intelligence" in this function that hops around based on the directional ** returns from editval isn't very smart, and depends on the layout above. **/ static void editparams(DEV_LIST *dev) { int ret; char buf[16]; /* needs to fit the device name */ putxy(2,17,"!bParameters!n-!bfor!n-!bdevice!n-"); sprintf(buf,"!b%s",dev->dev); putxy(24,17,buf); erase(1,22,80,1); for (;;) { ep_iobase: if (dev->iobase > 0) { #ifdef PC98 puthelp(" IO Port address (Hexadecimal, 0x1-0xffff)"); ret = editval(18,18,5,1,0x1,0xffff,&(dev->iobase),(dev->attrib & FLG_FIXIOBASE)); #else puthelp(" IO Port address (Hexadecimal, 0x1-0x2000)"); ret = editval(18,18,5,1,0x1,0x2000,&(dev->iobase),(dev->attrib & FLG_FIXIOBASE)); #endif switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_RIGHT: if (dev->maddr > 0) goto ep_maddr; break; case KEY_TAB: case KEY_DOWN: goto ep_irq; } goto ep_iobase; } ep_irq: if (dev->irq > 0) { puthelp(" Interrupt number (Decimal, 1-15)"); ret = editval(16,19,3,0,1,15,&(dev->irq),(dev->attrib & FLG_FIXIRQ)); switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_RIGHT: if (dev->msize > 0) goto ep_msize; break; case KEY_UP: if (dev->iobase > 0) goto ep_iobase; break; case KEY_TAB: case KEY_DOWN: goto ep_flags; } goto ep_irq; } ep_flags: puthelp(" Device-specific flag values."); ret = editval(18,20,5,1,0x0,0xffff,&(dev->flags),0); switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_RIGHT: if (dev->drq > 0) goto ep_drq; break; case KEY_UP: if (dev->irq > 0) goto ep_irq; if (dev->iobase > 0) goto ep_iobase; break; case KEY_DOWN: if (dev->maddr > 0) goto ep_maddr; if (dev->msize > 0) goto ep_msize; if (dev->drq > 0) goto ep_drq; break; case KEY_TAB: goto ep_maddr; } goto ep_flags; ep_maddr: if (dev->maddr > 0) { puthelp(" Device memory start address (Hexadecimal, 0x1-0xfffff)"); ret = editval(45,18,6,1,0x1,0xfffff,&(dev->maddr),(dev->attrib & FLG_FIXMADDR)); switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_LEFT: if (dev->iobase > 0) goto ep_iobase; break; case KEY_UP: goto ep_flags; case KEY_DOWN: if (dev->msize > 0) goto ep_msize; if (dev->drq > 0) goto ep_drq; break; case KEY_TAB: goto ep_msize; } goto ep_maddr; } ep_msize: if (dev->msize > 0) { puthelp(" Device memory size (Hexadecimal, 0x1-0x10000)"); ret = editval(45,19,5,1,0x1,0x10000,&(dev->msize),(dev->attrib & FLG_FIXMSIZE)); switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_LEFT: if (dev->irq > 0) goto ep_irq; break; case KEY_UP: if (dev->maddr > 0) goto ep_maddr; goto ep_flags; case KEY_DOWN: if (dev->drq > 0) goto ep_drq; break; case KEY_TAB: goto ep_drq; } goto ep_msize; } ep_drq: if (dev->drq > 0) { puthelp(" Device DMA request number (Decimal, 1-7)"); ret = editval(43,20,2,0,1,7,&(dev->drq),(dev->attrib & FLG_FIXDRQ)); switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_LEFT: goto ep_flags; case KEY_UP: if (dev->msize > 0) goto ep_msize; if (dev->maddr > 0) goto ep_maddr; goto ep_flags; case KEY_TAB: goto ep_iobase; } goto ep_drq; } } ep_exit: dev->changed = 1; /* mark as changed */ } /** ** High-level control functions **/ /** ** dolist ** ** Handle user movement within (*list) in the region starting at (row) onscreen with ** (num) lines, starting at (*ofs) offset from row onscreen. ** Pass (detail) on to drawing routines. ** ** If the user hits a key other than a cursor key, maybe return a code. ** ** (*list) points to the device at the top line in the region, (*ofs) is the ** position of the highlight within the region. All routines below ** this take only a device and an absolute row : use ofsent() to find the ** device, and add (*ofs) to (row) to find the absolute row. **/ static int dolist(int row, int num, int detail, int *ofs, DEV_LIST **list, char *dhelp) { int extended = 0; int c; DEV_LIST *lp; int delta = 1; for(;;) { if (delta) { showparams(ofsent(*ofs,*list)); /* show device parameters */ drawline(row+*ofs,detail,ofsent(*ofs,*list),1,dhelp); /* highlight current line */ delta = 0; } c = getchar(); /* get a character */ if ((extended == 2) || (c==588) || (c==596)) /* console gives "alternative" codes */ { extended = 0; /* no longer */ switch(c) { case 588: /* syscons' idea of 'up' */ case 'A': /* up */ if (*ofs) /* just a move onscreen */ { drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp);/* unhighlight current line */ (*ofs)--; /* move up */ }else{ lp = prevent(*list); /* can we go up? */ if (!lp) /* no */ break; *list = lp; /* yes, move up list */ drawlist(row,num,detail,*list); } delta = 1; break; case 596: /* dooby-do */ case 'B': /* down */ lp = ofsent(*ofs,*list); /* get current item */ if (!nextent(lp)) break; /* nothing more to move to */ drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp); /* unhighlight current line */ if (*ofs < (num-1)) /* room to move onscreen? */ { (*ofs)++; }else{ *list = nextent(*list); /* scroll region down */ drawlist(row,num,detail,*list); } delta = 1; break; } }else{ switch(c) { case '\033': extended=1; break; case '[': /* cheat : always preceeds cursor move */ case 'O': /* ANSI application key mode */ if (extended==1) extended=2; else extended=0; break; case 'Q': case 'q': return(KEY_EXIT); /* user requests exit */ case '\r': case '\n': return(KEY_DO); /* "do" something */ case '\b': case '\177': case 599: return(KEY_DEL); /* "delete" response */ case 'X': case 'x': return(KEY_UNZOOM); /* expand everything */ case 'C': case 'c': return(KEY_ZOOM); /* collapse everything */ case '\t': drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp); /* unhighlight current line */ return(KEY_TAB); /* "move" response */ case '\014': /* ^L, redraw */ return(KEY_REDRAW); } } } } /** ** visuserconfig ** ** Do the fullscreen config thang **/ static int visuserconfig(void) { int actofs = 0, inactofs = 0, mode = 0, ret = -1, i; DEV_LIST *dp; initlist(&active); initlist(&inactive); alist = active; ilist = inactive; getdevs(); conflicts = findconflict(active); /* find conflicts in the active list only */ redraw(); for(;;) { switch(mode) { case 0: /* active devices */ ret = dolist(1,8,1,&actofs,&alist, " [!bEnter!n] Edit device parameters [!bDEL!n] Disable device"); switch(ret) { case KEY_TAB: mode = 1; /* swap lists */ break; case KEY_REDRAW: redraw(); break; case KEY_ZOOM: alist = active; actofs = 0; expandlist(active); redrawactive(); break; case KEY_UNZOOM: alist = active; actofs = 0; collapselist(active); redrawactive(); break; case KEY_DEL: dp = ofsent(actofs,alist); /* get current device */ if (dp) /* paranoia... */ { if (dp->attrib & FLG_MANDATORY) /* can't be deleted */ break; if (dp == alist) /* moving top item on list? */ { if (dp->next) { alist = dp->next; /* point list to non-moving item */ }else{ alist = dp->prev; /* end of list, go back instead */ } }else{ if (!dp->next) /* moving last item on list? */ actofs--; } dp->conflicts = 0; /* no conflicts on the inactive list */ movedev(dp,inactive); /* shift to inactive list */ conflicts = findconflict(active); /* update conflict tags */ dp->changed = 1; redrawactive(); /* redraw */ redrawinactive(); } break; case KEY_DO: /* edit device parameters */ dp = ofsent(actofs,alist); /* get current device */ if (dp) /* paranoia... */ { if (dp->comment == DEV_DEVICE) /* can't edit comments, zoom? */ { masterhelp(" [!bTAB!n] Change fields [!bQ!n] Save device parameters"); editparams(dp); masterhelp(" [!bTAB!n] Change fields [!bQ!n] Save and Exit"); putxy(0,17,lines); conflicts = findconflict(active); /* update conflict tags */ }else{ /* DO on comment = zoom */ switch(dp->comment) /* Depends on current state */ { case DEV_COMMENT: /* not currently zoomed */ dp->comment = DEV_ZOOMED; break; case DEV_ZOOMED: dp->comment = DEV_COMMENT; break; } } redrawactive(); } break; } break; case 1: /* inactive devices */ ret = dolist(10,7,0,&inactofs,&ilist, " [!bEnter!n] Enable device "); switch(ret) { case KEY_TAB: mode = 0; break; case KEY_REDRAW: redraw(); break; case KEY_ZOOM: ilist = inactive; inactofs = 0; expandlist(inactive); redrawinactive(); break; case KEY_UNZOOM: ilist = inactive; inactofs = 0; collapselist(inactive); redrawinactive(); break; case KEY_DO: dp = ofsent(inactofs,ilist); /* get current device */ if (dp) /* paranoia... */ { if (dp->comment == DEV_DEVICE) /* can't move comments, zoom? */ { if (dp == ilist) /* moving top of list? */ { if (dp->next) { ilist = dp->next; /* point list to non-moving item */ }else{ ilist = dp->prev; /* can't go down, go up instead */ } }else{ if (!dp->next) /* last entry on list? */ inactofs--; /* shift cursor up one */ } movedev(dp,active); /* shift to active list */ conflicts = findconflict(active); /* update conflict tags */ dp->changed = 1; alist = dp; /* put at top and current */ actofs = 0; while(dp->comment == DEV_DEVICE) dp = dp->prev; /* forcibly unzoom section */ dp ->comment = DEV_COMMENT; mode = 0; /* and swap modes to follow it */ }else{ /* DO on comment = zoom */ switch(dp->comment) /* Depends on current state */ { case DEV_COMMENT: /* not currently zoomed */ dp->comment = DEV_ZOOMED; break; case DEV_ZOOMED: dp->comment = DEV_COMMENT; break; } } redrawactive(); /* redraw */ redrawinactive(); } break; default: /* nothing else relevant here */ break; } break; default: mode = 0; /* shouldn't happen... */ } if (ret == KEY_EXIT) { i = yesnocancel(" Save these parameters before exiting? (Yes/No/Cancel) "); switch(i) { case 2: /* cancel */ redraw(); break; case 1: /* save and exit */ savelist(active,1); savelist(inactive,0); case 0: /* exit */ nukelist(active); /* clean up after ourselves */ nukelist(inactive); normal(); clear(); return(1); } } } } /* * Copyright (c) 1991 Regents of the University of California. * All rights reserved. * Copyright (c) 1994 Jordan K. Hubbard * All rights reserved. * Copyright (c) 1994 David Greenman * All rights reserved. * * Many additional changes by Bruce Evans * * This code is derived from software contributed by the * University of California Berkeley, Jordan K. Hubbard, * David Greenman and Bruce Evans. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: userconfig.c,v 1.43 1996/08/10 22:06:09 joerg Exp $ */ #include "scbus.h" #include #define PARM_DEVSPEC 0x1 #define PARM_INT 0x2 #define PARM_ADDR 0x3 typedef struct _cmdparm { int type; union { struct isa_device *dparm; int iparm; void *aparm; } parm; } CmdParm; typedef int (*CmdFunc)(CmdParm *); typedef struct _cmd { char *name; CmdFunc handler; CmdParm *parms; } Cmd; #if NSCBUS > 0 static void lsscsi(void); static int list_scsi(CmdParm *); #endif static void lsdevtab(struct isa_device *); static struct isa_device *find_device(char *, int); static struct isa_device *search_devtable(struct isa_device *, char *, int); static void cngets(char *, int); static Cmd *parse_cmd(char *); static int parse_args(char *, CmdParm *); static unsigned long strtoul(const char *, char **, int); static int save_dev(struct isa_device *); static int list_devices(CmdParm *); static int set_device_ioaddr(CmdParm *); static int set_device_irq(CmdParm *); static int set_device_drq(CmdParm *); static int set_device_iosize(CmdParm *); static int set_device_mem(CmdParm *); static int set_device_flags(CmdParm *); static int set_device_enable(CmdParm *); static int set_device_disable(CmdParm *); static int quitfunc(CmdParm *); static int helpfunc(CmdParm *); static int lineno; static CmdParm addr_parms[] = { { PARM_DEVSPEC, {} }, { PARM_ADDR, {} }, { -1, {} }, }; static CmdParm int_parms[] = { { PARM_DEVSPEC, {} }, { PARM_INT, {} }, { -1, {} }, }; static CmdParm dev_parms[] = { { PARM_DEVSPEC, {} }, { -1, {} }, }; static Cmd CmdList[] = { { "?", helpfunc, NULL }, /* ? (help) */ { "di", set_device_disable, dev_parms }, /* disable dev */ { "dr", set_device_drq, int_parms }, /* drq dev # */ { "en", set_device_enable, dev_parms }, /* enable dev */ { "ex", quitfunc, NULL }, /* exit (quit) */ { "f", set_device_flags, int_parms }, /* flags dev mask */ { "h", helpfunc, NULL }, /* help */ { "iom", set_device_mem, addr_parms }, /* iomem dev addr */ { "ios", set_device_iosize, int_parms }, /* iosize dev size */ { "ir", set_device_irq, int_parms }, /* irq dev # */ { "l", list_devices, NULL }, /* ls, list */ { "po", set_device_ioaddr, int_parms }, /* port dev addr */ { "res", (CmdFunc)cpu_reset, NULL }, /* reset CPU */ { "q", quitfunc, NULL }, /* quit */ #if NSCBUS > 0 { "s", list_scsi, NULL }, /* scsi */ #endif { "v", (CmdFunc)visuserconfig, NULL }, /* visual mode */ { NULL, NULL, NULL }, }; void userconfig(void) { char input[80]; int rval; Cmd *cmd; #ifdef PC98 printf("\nFreeBSD(98) Kernel Configuration Utility - Version 1.0\n" " Type \"help\" for help or \"visual\" to go to the visual\n" " configuration interface.\n"); #else printf("\nFreeBSD Kernel Configuration Utility - Version 1.0\n" " Type \"help\" for help or \"visual\" to go to the visual\n" " configuration interface (requires MGA/VGA display or\n" " serial terminal capable of displaying ANSI graphics).\n"); #endif while (1) { printf("config> "); cngets(input, 80); if (input[0] == '\0') continue; cmd = parse_cmd(input); if (!cmd) { printf("Invalid command or syntax. Type `?' for help.\n"); continue; } rval = (*cmd->handler)(cmd->parms); if (rval) return; } } static Cmd * parse_cmd(char *cmd) { Cmd *cp; for (cp = CmdList; cp->name; cp++) { int len = strlen(cp->name); if (!strncmp(cp->name, cmd, len)) { while (*cmd && *cmd != ' ' && *cmd != '\t') ++cmd; if (parse_args(cmd, cp->parms)) return NULL; else return cp; } } return NULL; } static int parse_args(char *cmd, CmdParm *parms) { while (1) { char *ptr; if (*cmd == ' ' || *cmd == '\t') { ++cmd; continue; } if (parms == NULL || parms->type == -1) { if (*cmd == '\0') return 0; printf("Extra arg(s): %s\n", cmd); return 1; } if (parms->type == PARM_DEVSPEC) { int i = 0; char devname[64]; int unit = 0; while (*cmd && !(*cmd == ' ' || *cmd == '\t' || (*cmd >= '0' && *cmd <= '9'))) devname[i++] = *(cmd++); devname[i] = '\0'; if (*cmd >= '0' && *cmd <= '9') { unit = strtoul(cmd, &ptr, 10); if (cmd == ptr) { printf("Invalid device number\n"); /* XXX should print invalid token here and elsewhere. */ return 1; } /* XXX else should require end of token. */ cmd = ptr; } if ((parms->parm.dparm = find_device(devname, unit)) == NULL) { printf("No such device: %s%d\n", devname, unit); return 1; } ++parms; continue; } if (parms->type == PARM_INT) { parms->parm.iparm = strtoul(cmd, &ptr, 0); if (cmd == ptr) { printf("Invalid numeric argument\n"); return 1; } cmd = ptr; ++parms; continue; } if (parms->type == PARM_ADDR) { parms->parm.aparm = (void *)strtoul(cmd, &ptr, 0); if (cmd == ptr) { printf("Invalid address argument\n"); return 1; } cmd = ptr; ++parms; continue; } } return 0; } static int list_devices(CmdParm *parms) { lineno = 0; lsdevtab(&isa_devtab_bio[0]); lsdevtab(&isa_devtab_tty[0]); lsdevtab(&isa_devtab_net[0]); lsdevtab(&isa_devtab_null[0]); return 0; } static int set_device_ioaddr(CmdParm *parms) { parms[0].parm.dparm->id_iobase = parms[1].parm.iparm; save_dev(parms[0].parm.dparm); return 0; } static int set_device_irq(CmdParm *parms) { unsigned irq; irq = parms[1].parm.iparm; #ifndef PC98 if (irq == 2) { printf("Warning: Remapping IRQ 2 to IRQ 9 - see config(8)\n"); irq = 9; } else if (irq != -1 && irq > 15) { #else if (irq != -1 && irq > 15) { #endif printf("An IRQ > 15 would be invalid.\n"); return 0; } parms[0].parm.dparm->id_irq = (irq < 16 ? 1 << irq : 0); save_dev(parms[0].parm.dparm); return 0; } static int set_device_drq(CmdParm *parms) { unsigned drq; /* * The bounds checking is just to ensure that the value can be printed * in 5 characters. 32768 gets converted to -32768 and doesn't fit. */ drq = parms[1].parm.iparm; parms[0].parm.dparm->id_drq = (drq < 32768 ? drq : -1); save_dev(parms[0].parm.dparm); return 0; } static int set_device_iosize(CmdParm *parms) { parms[0].parm.dparm->id_msize = parms[1].parm.iparm; save_dev(parms[0].parm.dparm); return 0; } static int set_device_mem(CmdParm *parms) { parms[0].parm.dparm->id_maddr = parms[1].parm.aparm; save_dev(parms[0].parm.dparm); return 0; } static int set_device_flags(CmdParm *parms) { parms[0].parm.dparm->id_flags = parms[1].parm.iparm; save_dev(parms[0].parm.dparm); return 0; } static int set_device_enable(CmdParm *parms) { parms[0].parm.dparm->id_enabled = TRUE; save_dev(parms[0].parm.dparm); return 0; } static int set_device_disable(CmdParm *parms) { parms[0].parm.dparm->id_enabled = FALSE; save_dev(parms[0].parm.dparm); return 0; } static int quitfunc(CmdParm *parms) { return 1; } static int helpfunc(CmdParm *parms) { printf("Command\t\t\tDescription\n"); printf("-------\t\t\t-----------\n"); printf("ls\t\t\tList currently configured devices\n"); printf("port \tSet device port (i/o address)\n"); printf("irq \tSet device irq\n"); printf("drq \tSet device drq\n"); printf("iomem \tSet device maddr (memory address)\n"); printf("iosize \tSet device memory size\n"); printf("flags \tSet device flags\n"); printf("enable \tEnable device\n"); printf("disable \tDisable device (will not be probed)\n"); printf("quit\t\t\tExit this configuration utility\n"); printf("reset\t\t\tReset CPU\n"); printf("visual\t\t\tGo to fullscreen mode.\n"); printf("help\t\t\tThis message\n\n"); printf("Commands may be abbreviated to a unique prefix\n"); return 0; } static void lsdevtab(struct isa_device *dt) { for (; dt->id_id != 0; dt++) { int i; char line[80]; if (lineno >= 23) { printf(" "); (void)cngetc(); printf("\n"); lineno = 0; } if (lineno == 0) { printf( "Device port irq drq iomem iosize unit flags enabled\n"); ++lineno; } /* * printf() doesn't support %#, %- or even field widths for strings, * so formatting is not straightforward. */ bzero(line, sizeof line); sprintf(line, "%s%d", dt->id_driver->name, dt->id_unit); /* Missing: id_id (don't need it). */ /* Missing: id_driver (useful if we could show it by name). */ sprintf(line + 9, "0x%x", dt->id_iobase); sprintf(line + 20, "%d", ffs(dt->id_irq) - 1); sprintf(line + 26, "%d", dt->id_drq); sprintf(line + 32, "0x%x", dt->id_maddr); sprintf(line + 40, "%d", dt->id_msize); /* Missing: id_msize (0 at start, useful if we can get here later). */ /* Missing: id_intr (useful if we could show it by name). */ /* Display only: id_unit. */ sprintf(line + 49, "%d", dt->id_unit); sprintf(line + 55, "0x%x", dt->id_flags); /* Missing: id_scsiid, id_alive, id_ri_flags, id_reconfig (0 now...) */ sprintf(line + 66, "%s", dt->id_enabled ? "Yes" : "No"); for (i = 0; i < 66; ++i) if (line[i] == '\0') line[i] = ' '; printf("%s\n", line); ++lineno; } } static struct isa_device * find_device(char *devname, int unit) { struct isa_device *ret; if ((ret = search_devtable(&isa_devtab_bio[0], devname, unit)) != NULL) return ret; if ((ret = search_devtable(&isa_devtab_tty[0], devname, unit)) != NULL) return ret; if ((ret = search_devtable(&isa_devtab_net[0], devname, unit)) != NULL) return ret; if ((ret = search_devtable(&isa_devtab_null[0], devname, unit)) != NULL) return ret; return NULL; } static struct isa_device * search_devtable(struct isa_device *dt, char *devname, int unit) { int i; for (i = 0; dt->id_id != 0; dt++) if (!strcmp(dt->id_driver->name, devname) && dt->id_unit == unit) return dt; return NULL; } static void cngets(char *input, int maxin) { int c, nchars = 0; while (1) { c = cngetc(); /* Treat ^H or ^? as backspace */ if ((c == '\010' || c == '\177')) { if (nchars) { printf("\010 \010"); *--input = '\0', --nchars; } continue; } /* Treat ^U or ^X as kill line */ else if ((c == '\025' || c == '\030')) { while (nchars) { printf("\010 \010"); *--input = '\0', --nchars; } continue; } printf("%c", c); if ((++nchars == maxin) || (c == '\n') || (c == '\r')) { *input = '\0'; break; } *input++ = (u_char)c; } } /* * Kludges to get the library sources of strtoul.c to work in our * environment. isdigit() and isspace() could be used above too. */ #define isalpha(c) (((c) >= 'A' && (c) <= 'Z') \ || ((c) >= 'a' && (c) <= 'z')) /* unsafe */ #define isdigit(c) ((unsigned)((c) - '0') <= '9' - '0') #define isspace(c) ((c) == ' ' || (c) == '\t') /* unsafe */ #define isupper(c) ((unsigned)((c) - 'A') <= 'Z' - 'A') static int errno; /* * The following should be identical with the library sources for strtoul.c. */ /* * Convert a string to an unsigned long integer. * * Ignores `locale' stuff. Assumes that the upper and lower case * alphabets and digits are each contiguous. */ static unsigned long strtoul(nptr, endptr, base) const char *nptr; char **endptr; register int base; { register const char *s = nptr; register unsigned long acc; register int c; register unsigned long cutoff; register int neg = 0, any, cutlim; /* * See strtol for comments as to the logic used. */ do { c = *s++; } while (isspace(c)); if (c == '-') { neg = 1; c = *s++; } else if (c == '+') c = *s++; if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) { c = s[1]; s += 2; base = 16; } if (base == 0) base = c == '0' ? 8 : 10; cutoff = (unsigned long)ULONG_MAX / (unsigned long)base; cutlim = (unsigned long)ULONG_MAX % (unsigned long)base; for (acc = 0, any = 0;; c = *s++) { if (isdigit(c)) c -= '0'; else if (isalpha(c)) c -= isupper(c) ? 'A' - 10 : 'a' - 10; else break; if (c >= base) break; if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim) any = -1; else { any = 1; acc *= base; acc += c; } } if (any < 0) { acc = ULONG_MAX; errno = ERANGE; } else if (neg) acc = -acc; if (endptr != 0) *endptr = (char *)(any ? s - 1 : nptr); return (acc); } #if NSCBUS > 0 /* scsi: Support for displaying configured SCSI devices. * There is no way to edit them, and this is inconsistent * with the ISA method. This is here as a basis for further work. */ static char * type_text(char *name) /* XXX: This is bogus */ { if (strcmp(name, "sd") == 0) return "disk"; if (strcmp(name, "st") == 0) return "tape"; return "device"; } static void id_put(char *desc, int id) { if (id != SCCONF_UNSPEC) { if (desc) printf("%s", desc); if (id == SCCONF_ANY) printf("?"); else printf("%d", id); } } static void lsscsi(void) { int i; printf("scsi: (can't be edited):\n"); for (i = 0; scsi_cinit[i].driver; i++) { id_put("controller scbus", scsi_cinit[i].bus); if (scsi_cinit[i].unit != -1) { printf(" at "); id_put(scsi_cinit[i].driver, scsi_cinit[i].unit); } printf("\n"); } for (i = 0; scsi_dinit[i].name; i++) { printf("%s ", type_text(scsi_dinit[i].name)); id_put(scsi_dinit[i].name, scsi_dinit[i].unit); id_put(" at scbus", scsi_dinit[i].cunit); id_put(" target ", scsi_dinit[i].target); id_put(" lun ", scsi_dinit[i].lun); if (scsi_dinit[i].flags) printf("flags 0x%x\n", scsi_dinit[i].flags); printf("\n"); } } static int list_scsi(CmdParm *parms) { lineno = 0; lsscsi(); return 0; } #endif static int save_dev(idev) struct isa_device *idev; { struct isa_device *id_p,*id_pn; for (id_p=isa_devlist; id_p; id_p=id_p->id_next) { if (id_p->id_id == idev->id_id) { id_pn = id_p->id_next; bcopy(idev,id_p,sizeof(struct isa_device)); id_p->id_next = id_pn; return 1; } } id_pn = malloc(sizeof(struct isa_device),M_DEVL,M_WAITOK); bcopy(idev,id_pn,sizeof(struct isa_device)); id_pn->id_next = isa_devlist; isa_devlist = id_pn; return 0; }