kernel tree reorganization stage 1: Major cvs repository work (not logged as
[dragonfly.git] / sys / i386 / i386 / userconfig.c
1 /**
2  ** Copyright (c) 1995
3  **      Michael Smith, msmith@freebsd.org.  All rights reserved.
4  **
5  ** This code contains a module marked :
6
7  * Copyright (c) 1991 Regents of the University of California.
8  * All rights reserved.
9  * Copyright (c) 1994 Jordan K. Hubbard
10  * All rights reserved.
11  * Copyright (c) 1994 David Greenman
12  * All rights reserved.
13  *
14  * Many additional changes by Bruce Evans
15  *
16  * This code is derived from software contributed by the
17  * University of California Berkeley, Jordan K. Hubbard,
18  * David Greenman and Bruce Evans.
19
20  ** As such, it contains code subject to the above copyrights.
21  ** The module and its copyright can be found below.
22  ** 
23  ** Redistribution and use in source and binary forms, with or without
24  ** modification, are permitted provided that the following conditions
25  ** are met:
26  ** 1. Redistributions of source code must retain the above copyright
27  **    notice, this list of conditions and the following disclaimer as
28  **    the first lines of this file unmodified.
29  ** 2. Redistributions in binary form must reproduce the above copyright
30  **    notice, this list of conditions and the following disclaimer in the
31  **    documentation and/or other materials provided with the distribution.
32  ** 3. All advertising materials mentioning features or use of this software
33  **    must display the following acknowledgment:
34  **      This product includes software developed by Michael Smith.
35  ** 4. The name of the author may not be used to endorse or promote products
36  **    derived from this software without specific prior written permission.
37  **
38  ** THIS SOFTWARE IS PROVIDED BY MICHAEL SMITH ``AS IS'' AND ANY EXPRESS OR
39  ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
40  ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
41  ** IN NO EVENT SHALL MICHAEL SMITH BE LIABLE FOR ANY DIRECT, INDIRECT,
42  ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
43  ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
44  ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
45  ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46  ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
47  ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  **
49  ** $FreeBSD: src/sys/i386/i386/userconfig.c,v 1.175.2.10 2002/10/05 18:31:48 scottl Exp $
50  ** $DragonFly: src/sys/i386/i386/Attic/userconfig.c,v 1.3 2003/08/07 21:17:22 dillon Exp $
51  **/
52
53 /**
54  ** USERCONFIG
55  **
56  ** Kernel boot-time configuration manipulation tool for FreeBSD.
57  **
58  ** Two modes of operation are supported : the default is the line-editor mode,
59  ** the command "visual" invokes the fullscreen mode.
60  **
61  ** The line-editor mode is the old favorite from FreeBSD 2.0/20.05 &c., the 
62  ** fullscreen mode requires syscons or a minimal-ansi serial console.
63  **/
64
65 /**
66  ** USERCONFIG, visual mode.
67  **
68  **   msmith@freebsd.org
69  **
70  ** Look for "EDIT THIS LIST" to add to the list of known devices
71  ** 
72  **
73  ** There are a number of assumptions made in this code.
74  ** 
75  ** - That the console supports a minimal set of ANSI escape sequences
76  **   (See the screen manipulation section for a summary)
77  **   and has at least 24 rows.
78  ** - That values less than or equal to zero for any of the device
79  **   parameters indicate that the driver does not use the parameter.
80  ** - That flags are _always_ editable.
81  **
82  ** Devices marked as disabled are imported as such.
83  **
84  ** For this tool to be useful, the list of devices below _MUST_ be updated 
85  ** when a new driver is brought into the kernel.  It is not possible to 
86  ** extract this information from the drivers in the kernel.
87  **
88  ** XXX - TODO:
89  ** 
90  ** - Display _what_ a device conflicts with.
91  ** - Implement page up/down (as what?)
92  ** - Wizard mode (no restrictions)
93  ** - Find out how to put syscons back into low-intensity mode so that the
94  **   !b escape is useful on the console.  (It seems to be that it actually
95  **   gets low/high intensity backwards. That looks OK.)
96  **
97  ** - Only display headings with devices under them. (difficult)
98  **/
99
100 #include "opt_userconfig.h"
101 #define COMPAT_OLDISA   /* get the definitions */
102
103 #include <sys/param.h>
104 #include <sys/systm.h>
105 #include <sys/kernel.h>
106 #include <sys/malloc.h>
107 #include <sys/reboot.h>
108 #include <sys/linker.h>
109 #include <sys/sysctl.h>
110 #include <sys/bus.h>
111 #include <sys/cons.h>
112
113 #include <machine/md_var.h>
114 #include <machine/limits.h>
115
116 #define _I386_ISA_ISA_DEVICE_H_
117
118 #undef NPNP
119 #define NPNP 0
120
121 #if NPNP > 0
122 #include <i386/isa/pnp.h>
123 #endif
124
125 static MALLOC_DEFINE(M_DEVL, "uc_devlist", "uc_device lists in userconfig()");
126
127 #include <machine/uc_device.h>
128 static struct uc_device *uc_devlist;    /* list read by kget to extract changes */
129 static struct uc_device *uc_devtab;     /* fake uc_device table */
130
131 static int userconfig_boot_parsing;     /* set if we are reading from the boot instructions */
132
133 #define putchar(x)      cnputc(x)
134
135 static void load_devtab(void);
136 static void free_devtab(void);
137 static void save_resource(struct uc_device *);
138
139 static int
140 sysctl_machdep_uc_devlist(SYSCTL_HANDLER_ARGS)
141 {
142         struct uc_device *id;
143         int error=0;
144         char name[8];
145
146         if(!req->oldptr) {
147                 /* Only sizing */
148                 id=uc_devlist;
149                 while(id) {
150                         error+=sizeof(struct uc_device)+8;
151                         id=id->id_next;
152                 }
153                 return(SYSCTL_OUT(req,0,error));
154         } else {
155                 /* Output the data. The buffer is filled with consecutive
156                  * struct uc_device and char buf[8], containing the name
157                  * (not guaranteed to end with '\0').
158                  */
159                 id=uc_devlist;
160                 while(id) {
161                         error=sysctl_handle_opaque(oidp,id,
162                                 sizeof(struct uc_device),req);
163                         if(error) return(error);
164                         strncpy(name,id->id_name,8);
165                         error=sysctl_handle_opaque(oidp,name,
166                                 8,req);
167                         if(error) return(error);
168                         id=id->id_next;
169                 }
170                 return(0);
171         }
172 }
173
174 SYSCTL_PROC( _machdep, OID_AUTO, uc_devlist, CTLFLAG_RD,
175         0, 0, sysctl_machdep_uc_devlist, "A",
176         "List of ISA devices changed in UserConfig");
177
178 /*
179 ** Obtain command input.
180 **
181 ** Initially, input is read from a possibly-loaded script.
182 ** At the end of the script, or if no script is supplied, 
183 ** behaviour is determined by the RB_CONFIG (-c) flag.  If 
184 ** the flag is set, user input is read from the console; if
185 ** unset, the 'quit' command is invoked and userconfig
186 ** will exit.
187 **
188 ** Note that quit commands encountered in the script will be
189 ** ignored if the RB_CONFIG flag is supplied.
190 */
191 static const char       *config_script;
192 static int              config_script_size; /* use of int for -ve magic value */
193
194 #define has_config_script()     (config_script_size > 0)
195
196 static int
197 init_config_script(void)
198 {
199     caddr_t             autoentry, autoattr;
200
201     /* Look for loaded userconfig script */
202     autoentry = preload_search_by_type("userconfig_script");
203     if (autoentry != NULL) {
204         /* We have one, get size and data */
205         config_script_size = 0;
206         if ((autoattr = preload_search_info(autoentry, MODINFO_SIZE)) != NULL)
207             config_script_size = (size_t)*(u_int32_t *)autoattr;
208         config_script = NULL;
209         if ((autoattr = preload_search_info(autoentry, MODINFO_ADDR)) != NULL)
210             config_script = *(const char **)autoattr;
211         /* sanity check */
212         if ((config_script_size == 0) || (config_script == NULL)) {
213             config_script_size = 0;
214             config_script = NULL;
215         }
216     }
217     return has_config_script();
218 }
219
220 static int
221 getchar(void)
222 {
223     int                 c = -1;
224 #ifdef INTRO_USERCONFIG
225     static int          intro = 0;
226 #endif
227     
228     if (has_config_script()) 
229     {
230         /* Consume character from loaded userconfig script, display */
231         userconfig_boot_parsing = 1;
232         c = *config_script;
233         config_script++;
234         config_script_size--;
235
236     } else {
237         
238 #ifdef INTRO_USERCONFIG
239         if (userconfig_boot_parsing) {
240             if (!(boothowto & RB_CONFIG)) {
241                 /* userconfig_script, !RB_CONFIG -> quit */
242                 if (intro == 0) {
243                     c = 'q';
244                     config_script = "uit\n";
245                     config_script_size = strlen(config_script);
246                     /* userconfig_script will be 1 on the next pass */
247                 }
248             } else {
249                 /* userconfig_script, RB_CONFIG -> cngetc() */
250             }
251         } else {
252             if (!(boothowto & RB_CONFIG)) {
253                 /* no userconfig_script, !RB_CONFIG -> show intro */
254                 if (intro == 0) {
255                     intro = 1;
256                     c = 'i';
257                     config_script = "ntro\n";
258                     config_script_size = strlen(config_script);
259                     /* userconfig_script will be 1 on the next pass */
260                 }
261             } else {
262                 /* no userconfig_script, RB_CONFIG -> cngetc() */
263             }
264         }
265 #else /* !INTRO_USERCONFIG */
266         /* assert(boothowto & RB_CONFIG) */
267 #endif /* INTRO_USERCONFIG */
268         userconfig_boot_parsing = 0;
269         if (c <= 0)
270             c = cngetc();
271     }
272     return(c);
273 }
274
275 #ifndef FALSE
276 #define FALSE   (0)
277 #define TRUE    (!FALSE)
278 #endif
279
280 #ifdef VISUAL_USERCONFIG
281
282 typedef struct
283 {
284     char        dev[16];                /* device basename */
285     char        name[60];               /* long name */
286     int         attrib;                 /* things to do with the device */
287     int         class;                  /* device classification */
288 } DEV_INFO;
289
290 #define FLG_INVISIBLE   (1<<0)          /* device should not be shown */
291 #define FLG_MANDATORY   (1<<1)          /* device can be edited but not disabled */
292 #define FLG_FIXIRQ      (1<<2)          /* device IRQ cannot be changed */
293 #define FLG_FIXIOBASE   (1<<3)          /* device iobase cannot be changed */
294 #define FLG_FIXMADDR    (1<<4)          /* device maddr cannot be changed */
295 #define FLG_FIXMSIZE    (1<<5)          /* device msize cannot be changed */
296 #define FLG_FIXDRQ      (1<<6)          /* device DRQ cannot be changed */
297 #define FLG_FIXED       (FLG_FIXIRQ|FLG_FIXIOBASE|FLG_FIXMADDR|FLG_FIXMSIZE|FLG_FIXDRQ)
298 #define FLG_IMMUTABLE   (FLG_FIXED|FLG_MANDATORY)
299
300 #define CLS_STORAGE     1               /* storage devices */
301 #define CLS_NETWORK     2               /* network interfaces */
302 #define CLS_COMMS       3               /* serial, parallel ports */
303 #define CLS_INPUT       4               /* user input : mice, keyboards, joysticks etc */
304 #define CLS_MMEDIA      5               /* "multimedia" devices (sound, video, etc) */
305 #define CLS_MISC        255             /* none of the above */
306
307
308 typedef struct 
309 {
310     char        name[60];
311     int         number;
312 } DEVCLASS_INFO;
313
314 static DEVCLASS_INFO devclass_names[] = {
315 {       "Storage :        ",    CLS_STORAGE},
316 {       "Network :        ",    CLS_NETWORK},
317 {       "Communications : ",    CLS_COMMS},
318 {       "Input :          ",    CLS_INPUT},
319 {       "Multimedia :     ",    CLS_MMEDIA},
320 {       "Miscellaneous :  ",    CLS_MISC},
321 {       "",0}};
322
323
324 /********************* EDIT THIS LIST **********************/
325
326 /** Notes :
327  ** 
328  ** - Devices that shouldn't be seen or removed should be marked FLG_INVISIBLE.
329  ** - XXX The list below should be reviewed by the driver authors to verify
330  **   that the correct flags have been set for each driver, and that the
331  **   descriptions are accurate.
332  **/
333
334 static DEV_INFO device_info[] = {
335 /*---Name-----   ---Description---------------------------------------------- */
336 {"adv",         "AdvanSys SCSI narrow controller",      0,              CLS_STORAGE},
337 {"bt",          "Buslogic SCSI controller",             0,              CLS_STORAGE},
338 {"aha",         "Adaptec 154x SCSI controller",         0,              CLS_STORAGE},
339 {"aic",         "Adaptec 152x SCSI and compatible sound cards", 0,      CLS_STORAGE},
340 {"nca",         "ProAudio Spectrum SCSI and compatibles",       0,      CLS_STORAGE},
341 {"sea",         "Seagate ST01/ST02 SCSI and compatibles",       0,      CLS_STORAGE},
342 {"stg",         "TMC 18C30/18C50 based SCSI cards",     0,              CLS_STORAGE},
343 {"wdc",         "IDE/ESDI/MFM disk controller",         0,              CLS_STORAGE},
344 {"ata",         "ATA/ATAPI compatible disk controller", 0,              CLS_STORAGE},
345 {"fdc",         "Floppy disk controller",               FLG_FIXED,      CLS_STORAGE},
346 {"mcd",         "Mitsumi CD-ROM",                       0,              CLS_STORAGE},
347 {"scd",         "Sony CD-ROM",                          0,              CLS_STORAGE},
348 {"wt",          "Wangtek/Archive QIC-02 Tape drive",    0,              CLS_STORAGE},
349 {"wd",          "IDE or ST506 compatible storage device", FLG_INVISIBLE, CLS_STORAGE},
350 {"ad",          "ATA/ATAPI compatible storage device",  FLG_INVISIBLE,  CLS_STORAGE},   
351 {"fd",          "Floppy disk device",                   FLG_INVISIBLE,  CLS_STORAGE},
352
353 {"cm",          "SMC COM90Cx6-based Arcnet adapters",   0,              CLS_NETWORK},
354 {"cs",          "IBM EtherJet, CS89x0-based Ethernet adapters",0,       CLS_NETWORK},
355 {"ed",          "NE1000,NE2000,3C503,WD/SMC80xx Ethernet adapters",0,   CLS_NETWORK},
356 {"el",          "3C501 Ethernet adapter",               0,              CLS_NETWORK},
357 {"ep",          "3C509 Ethernet adapter",               0,              CLS_NETWORK},
358 {"ex",          "Intel EtherExpress Pro/10 Ethernet adapter",   0,      CLS_NETWORK},
359 {"fe",          "Fujitsu MB86960A/MB86965A Ethernet adapters",  0,      CLS_NETWORK},
360 {"ie",          "AT&T Starlan 10 and EN100, 3C507, NI5210 Ethernet adapters",0,CLS_NETWORK},
361 {"le",          "DEC Etherworks 2 and 3 Ethernet adapters",     0,      CLS_NETWORK},
362 {"lnc",         "Isolan, Novell NE2100/NE32-VL Ethernet adapters",      0,CLS_NETWORK},
363 {"sn",          "SMC/Megahertz Ethernet adapters",                      0,CLS_NETWORK},
364 {"xe",          "Xircom PC Card Ethernet adapter",              0,      CLS_NETWORK},
365 {"rdp",         "RealTek RTL8002 Pocket Ethernet",      0,              CLS_NETWORK},
366 {"sbni",        "Granch SBNI12-xx adapters",            0,              CLS_NETWORK},
367
368 {"sio",         "8250/16450/16550 Serial port",         0,              CLS_COMMS},
369 {"cx",          "Cronyx/Sigma multiport sync/async adapter",0,          CLS_COMMS},
370 {"rc",          "RISCom/8 multiport async adapter",     0,              CLS_COMMS},
371 {"cy",          "Cyclades multiport async adapter",     0,              CLS_COMMS},
372 {"dgb",         "Digiboard PC/Xe, PC/Xi async adapter", 0,              CLS_COMMS},
373 {"si",          "Specialix SI/XIO/SX async adapter",    0,              CLS_COMMS},
374 {"stl",         "Stallion EasyIO/Easy Connection 8/32 async adapter",0, CLS_COMMS},
375 {"stli",        "Stallion intelligent async adapter"    ,0,             CLS_COMMS},
376 {"ppc",         "Parallel Port chipset",                0,              CLS_COMMS},
377 {"gp",          "National Instruments AT-GPIB/TNT driver",      0,      CLS_COMMS},
378
379 {"atkbdc",      "Keyboard controller",                  FLG_INVISIBLE,  CLS_INPUT},
380 {"atkbd",       "Keyboard",                             FLG_FIXED,      CLS_INPUT},
381 {"mse",         "Microsoft Bus Mouse",                  0,              CLS_INPUT},
382 {"psm",         "PS/2 Mouse",                           FLG_FIXED,      CLS_INPUT},
383 {"joy",         "Joystick",                             FLG_FIXED,      CLS_INPUT},
384 {"vt",          "PCVT console driver",                  FLG_IMMUTABLE,  CLS_INPUT},
385 {"sc",          "Syscons console driver",               FLG_IMMUTABLE,  CLS_INPUT},
386
387 {"sbc",         "PCM Creative SoundBlaster/ESS/Avance sounce cards",    0,CLS_MMEDIA},
388 {"gusc",        "PCM Gravis UltraSound sound cards",    0,              CLS_MMEDIA},
389 {"pcm",         "PCM Generic soundcard support",                0,              CLS_MMEDIA},
390 {"sb",          "VOXWARE Soundblaster PCM (SB/Pro/16, ProAudio Spectrum)",0,CLS_MMEDIA},
391 {"sbxvi",       "VOXWARE Soundblaster 16",              0,              CLS_MMEDIA},
392 {"sbmidi",      "VOXWARE Soundblaster MIDI interface",  0,              CLS_MMEDIA},
393 {"awe",         "VOXWARE AWE32 MIDI",                   0,              CLS_MMEDIA},
394 {"pas",         "VOXWARE ProAudio Spectrum PCM and MIDI",       0,      CLS_MMEDIA},
395 {"gus",         "VOXWARE Gravis Ultrasound, Ultrasound 16 and Ultrasound MAX",0,CLS_MMEDIA},
396 {"gusxvi",      "VOXWARE Gravis Ultrasound 16-bit PCM", 0,              CLS_MMEDIA},
397 {"gusmax",      "VOXWARE Gravis Ultrasound MAX",        0,              CLS_MMEDIA},
398 {"mss",         "VOXWARE Microsoft Sound System",       0,              CLS_MMEDIA},
399 {"opl",         "VOXWARE OPL-2/3 FM, SB/Pro/16, ProAudio Spectrum",0,CLS_MMEDIA},
400 {"mpu",         "VOXWARE Roland MPU401 MIDI",           0,              CLS_MMEDIA},
401 {"sscape",      "VOXWARE Ensoniq Soundscape MIDI interface",    0,      CLS_MMEDIA},
402 {"sscape_mss",  "VOXWARE Ensoniq Soundscape PCM",       0,              CLS_MMEDIA},
403 {"uart",        "VOXWARE 6850 MIDI UART",               0,              CLS_MMEDIA},
404 {"pca",         "PC speaker PCM audio driver",          FLG_FIXED,      CLS_MMEDIA},
405 {"ctx",         "Coretex-I frame grabber",              0,              CLS_MMEDIA},
406 {"spigot",      "Creative Labs Video Spigot video capture",     0,      CLS_MMEDIA},
407 {"scc",         "IBM Smart Capture Card",               0,              CLS_MMEDIA},
408 {"gsc",         "Genius GS-4500 hand scanner",          0,              CLS_MMEDIA},
409 {"asc",         "AmiScan scanner",                      0,              CLS_MMEDIA},
410
411 {"apm",         "Advanced Power Management",            FLG_FIXED,      CLS_MISC},
412 {"labpc",       "National Instruments Lab-PC/Lab-PC+",  0,              CLS_MISC},
413 {"pcic",        "PC-card controller",                   0,              CLS_MISC},
414 {"npx",         "Math coprocessor",                     FLG_IMMUTABLE,  CLS_MISC},
415 {"vga",         "Catchall PCI VGA driver",              FLG_INVISIBLE,  CLS_MISC},
416 {"","",0,0}};
417
418
419 typedef struct _devlist_struct
420 {
421     char        name[80];
422     int         attrib;                 /* flag values as per the FLG_* defines above */
423     int         class;                  /* disk, etc as per the CLS_* defines above */
424     char        dev[16];
425     int         iobase,irq,drq,maddr,msize,unit,flags,id;
426     int         comment;                /* 0 = device, 1 = comment, 2 = collapsed comment */
427     int         conflicts;              /* set/reset by findconflict, count of conflicts */
428     int         changed;                /* nonzero if the device has been edited */
429     struct uc_device    *device;
430     struct _devlist_struct *prev,*next;
431 } DEV_LIST;
432
433
434 #define DEV_DEVICE      0
435 #define DEV_COMMENT     1
436 #define DEV_ZOOMED      2
437
438 #define LIST_CURRENT    (1<<0)
439 #define LIST_SELECTED   (1<<1)
440
441 #define KEY_EXIT        0       /* return codes from dolist() and friends */
442 #define KEY_DO          1
443 #define KEY_DEL         2
444 #define KEY_TAB         3
445 #define KEY_REDRAW      4
446
447 #define KEY_UP          5       /* these only returned from editval() */
448 #define KEY_DOWN        6
449 #define KEY_LEFT        7
450 #define KEY_RIGHT       8
451 #define KEY_NULL        9       /* this allows us to spin & redraw */
452
453 #define KEY_ZOOM        10      /* these for zoom all/collapse all */
454 #define KEY_UNZOOM      11
455
456 #define KEY_HELP        12      /* duh? */
457
458 static void redraw(void);
459 static void insdev(DEV_LIST *dev, DEV_LIST *list);
460 static int  devinfo(DEV_LIST *dev);
461 static int  visuserconfig(void);
462
463 static DEV_LIST *active = NULL,*inactive = NULL;        /* driver lists */
464 static DEV_LIST *alist,*ilist;                          /* visible heads of the driver lists */
465 static DEV_LIST scratch;                                /* scratch record */
466 static int      conflicts;                              /* total conflict count */
467
468
469 static char lines[] = "--------------------------------------------------------------------------------";
470 static char spaces[] = "                                                                                     ";
471
472
473 /**
474  ** Device manipulation stuff : find, describe, configure.
475  **/
476
477 /**
478  ** setdev
479  **
480  ** Sets the device referenced by (*dev) to the parameters in the struct,
481  ** and the enable flag according to (enabled)
482  **/
483 static void 
484 setdev(DEV_LIST *dev, int enabled)
485 {
486     dev->device->id_iobase = dev->iobase;                               /* copy happy */
487     dev->device->id_irq = (u_short)(dev->irq < 16 ? 1<<dev->irq : 0);   /* IRQ is bitfield */
488     dev->device->id_drq = (short)dev->drq;
489     dev->device->id_maddr = (caddr_t)dev->maddr;
490     dev->device->id_msize = dev->msize;
491     dev->device->id_flags = dev->flags;
492     dev->device->id_enabled = enabled;
493 }
494
495
496 /**
497  ** getdevs
498  **
499  ** Walk the kernel device tables and build the active and inactive lists
500  **/
501 static void 
502 getdevs(void)
503 {
504     int                 i;
505     struct uc_device    *ap;
506
507         ap = uc_devtab;                         /* pointer to array of devices */
508         for (i = 0; ap[i].id_id; i++)                   /* for each device in this table */
509         {
510             scratch.unit = ap[i].id_unit;               /* device parameters */
511             strcpy(scratch.dev,ap[i].id_name);
512             scratch.iobase = ap[i].id_iobase;
513             scratch.irq = ffs(ap[i].id_irq)-1;
514             scratch.drq = ap[i].id_drq;
515             scratch.maddr = (int)ap[i].id_maddr;
516             scratch.msize = ap[i].id_msize;
517             scratch.flags = ap[i].id_flags;
518
519             scratch.comment = DEV_DEVICE;               /* admin stuff */
520             scratch.conflicts = 0;
521             scratch.device = &ap[i];                    /* save pointer for later reference */
522             scratch.changed = 0;
523             if (!devinfo(&scratch))                     /* get more info on the device */
524                 insdev(&scratch,ap[i].id_enabled?active:inactive);
525         }
526 }
527
528
529 /**
530  ** Devinfo
531  **
532  ** Fill in (dev->name), (dev->attrib) and (dev->type) from the device_info array.
533  ** If the device is unknown, put it in the CLS_MISC class, with no flags.
534  **
535  ** If the device is marked "invisible", return nonzero; the caller should
536  ** not insert any such device into either list.
537  **
538  **/
539 static int
540 devinfo(DEV_LIST *dev)
541 {
542     int         i;
543
544     for (i = 0; device_info[i].class; i++)
545     {
546         if (!strcmp(dev->dev,device_info[i].dev))
547         {
548             if (device_info[i].attrib & FLG_INVISIBLE)  /* forget we ever saw this one */
549                 return(1);
550             strcpy(dev->name,device_info[i].name);      /* get the name */
551             dev->attrib = device_info[i].attrib;
552             dev->class = device_info[i].class;
553             return(0);
554         }
555     }
556     strcpy(dev->name,"Unknown device");
557     dev->attrib = 0;
558     dev->class = CLS_MISC;
559     return(0);
560 }
561     
562
563 /**
564  ** List manipulation stuff : add, move, initialise, free, traverse
565  **
566  ** Note that there are assumptions throughout this code that
567  ** the first entry in a list will never move. (assumed to be
568  ** a comment).
569  **/
570
571
572 /**
573  ** Adddev
574  ** 
575  ** appends a copy of (dev) to the end of (*list)
576  **/
577 static void 
578 addev(DEV_LIST *dev, DEV_LIST **list)
579 {
580
581     DEV_LIST    *lp,*ap;
582
583     lp = (DEV_LIST *)malloc(sizeof(DEV_LIST),M_DEVL,M_WAITOK);
584     bcopy(dev,lp,sizeof(DEV_LIST));                     /* create copied record */
585
586     if (*list)                                          /* list exists */
587     {
588         ap = *list;
589         while(ap->next)
590             ap = ap->next;                              /* scoot to end of list */
591         lp->prev = ap;
592         lp->next = NULL;
593         ap->next = lp;
594     }else{                                              /* list does not yet exist */
595         *list = lp;
596         lp->prev = lp->next = NULL;                     /* list now exists */
597     }
598 }
599
600
601 /**
602  ** Findspot
603  **
604  ** Finds the 'appropriate' place for (dev) in (list)
605  **
606  ** 'Appropriate' means in numeric order with other devices of the same type,
607  ** or in alphabetic order following a comment of the appropriate type.
608  ** or at the end of the list if an appropriate comment is not found. (this should
609  ** never happen)
610  ** (Note that the appropriate point is never the top, but may be the bottom)
611  **/
612 static DEV_LIST *
613 findspot(DEV_LIST *dev, DEV_LIST *list)
614 {
615     DEV_LIST    *ap = NULL;
616
617     /* search for a previous instance of the same device */
618     for (ap = list; ap; ap = ap->next)
619     {
620         if (ap->comment != DEV_DEVICE)                  /* ignore comments */
621             continue;
622         if (!strcmp(dev->dev,ap->dev))                  /* same base device */
623         {
624             if ((dev->unit <= ap->unit)                 /* belongs before (equal is bad) */
625                 || !ap->next)                           /* or end of list */
626             {
627                 ap = ap->prev;                          /* back up one */
628                 break;                                  /* done here */
629             }
630             if (ap->next)                                       /* if the next item exists */
631             {
632                 if (ap->next->comment != DEV_DEVICE)    /* next is a comment */
633                     break;
634                 if (strcmp(dev->dev,ap->next->dev))             /* next is a different device */
635                     break;
636             }
637         }
638     }
639
640     if (!ap)                                            /* not sure yet */
641     {
642         /* search for a class that the device might belong to */
643         for (ap = list; ap; ap = ap->next)
644         {
645             if (ap->comment != DEV_DEVICE)              /* look for simlar devices */
646                 continue;
647             if (dev->class != ap->class)                /* of same class too 8) */
648                 continue;
649             if (strcmp(dev->dev,ap->dev) < 0)           /* belongs before the current entry */
650             {
651                 ap = ap->prev;                          /* back up one */
652                 break;                                  /* done here */
653             }
654             if (ap->next)                               /* if the next item exists */
655                 if (ap->next->comment != DEV_DEVICE)    /* next is a comment, go here */
656                     break;
657         }
658     }
659
660     if (!ap)                                            /* didn't find a match */
661     {
662         for (ap = list; ap->next; ap = ap->next)        /* try for a matching comment */
663             if ((ap->comment != DEV_DEVICE) 
664                 && (ap->class == dev->class))           /* appropriate place? */
665                 break;
666     }                                                   /* or just put up with last */
667
668     return(ap);
669 }
670
671
672 /**
673  ** Insdev
674  **
675  ** Inserts a copy of (dev) at the appropriate point in (list)
676  **/
677 static void 
678 insdev(DEV_LIST *dev, DEV_LIST *list)
679 {
680     DEV_LIST    *lp,*ap;
681
682     lp = (DEV_LIST *)malloc(sizeof(DEV_LIST),M_DEVL,M_WAITOK);
683     bcopy(dev,lp,sizeof(DEV_LIST));                     /* create copied record */
684
685     ap = findspot(lp,list);                             /* find appropriate spot */
686     lp->next = ap->next;                                /* point to next */
687     if (ap->next)
688         ap->next->prev = lp;                            /* point next to new */
689     lp->prev = ap;                                      /* point new to current */
690     ap->next = lp;                                      /* and current to new */
691 }
692
693
694 /**
695  ** Movedev
696  **
697  ** Moves (dev) from its current list to an appropriate place in (list)
698  ** (dev) may not come from the top of a list, but it may from the bottom.
699  **/
700 static void 
701 movedev(DEV_LIST *dev, DEV_LIST *list)
702 {
703     DEV_LIST    *ap;
704
705     ap = findspot(dev,list);
706     dev->prev->next = dev->next;                        /* remove from old list */
707     if (dev->next)
708         dev->next->prev = dev->prev;
709     
710     dev->next = ap->next;                               /* insert in new list */
711     if (ap->next)
712         ap->next->prev = dev;                           /* point next to new */
713     dev->prev = ap;                                     /* point new to current */
714     ap->next = dev;                                     /* and current to new */
715 }
716
717
718 /**
719  ** Initlist
720  **
721  ** Initialises (*list) with the basic headings
722  **/
723 static void 
724 initlist(DEV_LIST **list)
725 {
726     int         i;
727
728     for(i = 0; devclass_names[i].name[0]; i++)          /* for each devtype name */
729     {
730         strcpy(scratch.name,devclass_names[i].name);
731         scratch.comment = DEV_ZOOMED;
732         scratch.class = devclass_names[i].number;
733         scratch.attrib = FLG_MANDATORY;                 /* can't be moved */
734         addev(&scratch,list);                           /* add to the list */
735     }
736 }
737
738
739 /**
740  ** savelist
741  **
742  ** Walks (list) and saves the settings of any entry marked as changed.
743  **
744  ** The device's active field is set according to (active).
745  **
746  ** Builds the uc_devlist used by kget to extract the changed device information.
747  ** The code for this was taken almost verbatim from the original module.
748  **/
749 static void
750 savelist(DEV_LIST *list, int active)
751 {
752     struct uc_device    *id_p,*id_pn;
753     char *name;
754
755     while (list)
756     {
757         if ((list->comment == DEV_DEVICE) &&            /* is a device */
758             (list->changed) &&                          /* has been changed */
759             (list->device != NULL)) {                   /* has an uc_device structure */
760
761             setdev(list,active);                        /* set the device itself */
762
763             id_pn = NULL;
764             for (id_p=uc_devlist; id_p; id_p=id_p->id_next) 
765             {                                           /* look on the list for it */
766                 if (id_p->id_id == list->device->id_id) 
767                 {
768                     name = list->device->id_name;
769                     id_pn = id_p->id_next;
770                     if (id_p->id_name)
771                             free(id_p->id_name, M_DEVL);
772                     bcopy(list->device,id_p,sizeof(struct uc_device));
773                     save_resource(list->device);
774                     id_p->id_name = malloc(strlen(name) + 1, M_DEVL,M_WAITOK);
775                     strcpy(id_p->id_name, name);
776                     id_pn->id_next = uc_devlist;
777                     id_p->id_next = id_pn;
778                     break;
779                 }
780             }
781             if (!id_pn)                                 /* not already on the list */
782             {
783                 name = list->device->id_name;
784                 id_pn = malloc(sizeof(struct uc_device),M_DEVL,M_WAITOK);
785                 bcopy(list->device,id_pn,sizeof(struct uc_device));
786                 save_resource(list->device);
787                 id_pn->id_name = malloc(strlen(name) + 1, M_DEVL,M_WAITOK);
788                 strcpy(id_pn->id_name, name);
789                 id_pn->id_next = uc_devlist;
790                 uc_devlist = id_pn;                     /* park at top of list */
791             }
792         }
793         list = list->next;
794     }
795 }
796
797
798 /**
799  ** nukelist
800  **
801  ** Frees all storage in use by a (list).
802  **/
803 static void 
804 nukelist(DEV_LIST *list)
805 {
806     DEV_LIST    *dp;
807
808     if (!list)
809         return;
810     while(list->prev)                                   /* walk to head of list */
811         list = list->prev;
812
813     while(list)
814     {
815         dp = list;
816         list = list->next;
817         free(dp,M_DEVL);
818     }
819 }
820
821
822 /**
823  ** prevent
824  **
825  ** Returns the previous entry in (list), skipping zoomed regions.  Returns NULL
826  ** if there is no previous entry. (Only possible if list->prev == NULL given the
827  ** premise that there is always a comment at the head of the list)
828  **/
829 static DEV_LIST *
830 prevent(DEV_LIST *list)
831 {
832     DEV_LIST    *dp;
833
834     if (!list)
835         return(NULL);
836     dp = list->prev;                    /* start back one */
837     while(dp)
838     {
839         if (dp->comment == DEV_ZOOMED)  /* previous section is zoomed */
840             return(dp);                 /* so skip to comment */
841         if (dp->comment == DEV_COMMENT) /* not zoomed */
842             return(list->prev);         /* one back as normal */
843         dp = dp->prev;                  /* backpedal */
844     }
845     return(dp);                         /* NULL, we can assume */
846 }
847
848
849 /**
850  ** nextent
851  **
852  ** Returns the next entry in (list), skipping zoomed regions.  Returns NULL
853  ** if there is no next entry. (Possible if the current entry is last, or
854  ** if the current entry is the last heading and it's collapsed)
855  **/
856 static DEV_LIST *
857 nextent(DEV_LIST *list)
858 {
859     DEV_LIST    *dp;
860
861     if (!list)
862         return(NULL);
863     if (list->comment != DEV_ZOOMED)    /* no reason to skip */
864         return(list->next);
865     dp = list->next;
866     while(dp)
867     {
868         if (dp->comment != DEV_DEVICE)  /* found another heading */
869             break;
870         dp = dp->next;
871     }
872     return(dp);                         /* back we go */
873 }
874     
875
876 /**
877  ** ofsent
878  **
879  ** Returns the (ofs)th entry down from (list), or NULL if it doesn't exist 
880  **/
881 static DEV_LIST *
882 ofsent(int ofs, DEV_LIST *list)
883 {
884     while (ofs-- && list)
885         list = nextent(list);
886     return(list);
887 }
888
889
890 /**
891  ** findconflict
892  **
893  ** Scans every element of (list) and sets the conflict tags appropriately
894  ** Returns the number of conflicts found.
895  **/
896 static int
897 findconflict(DEV_LIST *list)
898 {
899     int         count = 0;                      /* number of conflicts found */
900     DEV_LIST    *dp,*sp;
901
902     for (dp = list; dp; dp = dp->next)          /* over the whole list */
903     {
904         if (dp->comment != DEV_DEVICE)          /* comments don't usually conflict */
905             continue;
906
907         dp->conflicts = 0;                      /* assume the best */
908         for (sp = list; sp; sp = sp->next)      /* scan the entire list for conflicts */
909         {
910             if (sp->comment != DEV_DEVICE)      /* likewise */
911                 continue;
912
913             if (sp == dp)                       /* always conflict with itself */
914                 continue;
915
916             if ((dp->iobase > 0) &&             /* iobase conflict? */
917                 (dp->iobase == sp->iobase))
918                 dp->conflicts = 1;
919             if ((dp->irq > 0) &&                /* irq conflict? */
920                 (dp->irq == sp->irq))
921                 dp->conflicts = 1;
922             if ((dp->drq > 0) &&                /* drq conflict? */
923                 (dp->drq == sp->drq))
924                 dp->conflicts = 1;
925             if ((sp->maddr > 0) &&              /* maddr/msize conflict? */
926                 (dp->maddr > 0) &&
927                 (sp->maddr + ((sp->msize == 0) ? 1 : sp->msize) > dp->maddr) &&
928                 (dp->maddr + ((dp->msize == 0) ? 1 : dp->msize) > sp->maddr))
929                 dp->conflicts = 1;
930         }
931         count += dp->conflicts;                 /* count conflicts */
932     }
933     return(count);
934 }
935
936
937 /**
938  ** expandlist
939  **
940  ** Unzooms all headings in (list)
941  **/
942 static void
943 expandlist(DEV_LIST *list)
944 {
945     while(list)
946     {
947         if (list->comment == DEV_COMMENT)
948             list->comment = DEV_ZOOMED;
949         list = list->next;
950     }
951 }
952
953
954 /**
955  ** collapselist
956  **
957  ** Zooms all headings in (list)
958  **/
959 static void
960 collapselist(DEV_LIST *list)
961 {
962     while(list)
963     {
964         if (list->comment == DEV_ZOOMED)
965             list->comment = DEV_COMMENT;
966         list = list->next;
967     }
968 }
969
970
971 /**
972  ** Screen-manipulation stuff
973  **
974  ** This is the basic screen layout :
975  **
976  **     0    5   10   15   20   25   30   35   40   45   50   55   60   67   70   75
977  **     |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|....
978  **    +--------------------------------------------------------------------------------+
979  ** 0 -|---Active Drivers----------------------------xx Conflicts------Dev---IRQ--Port--|
980  ** 1 -| ........................                                    .......  ..  0x....|
981  ** 2 -| ........................                                    .......  ..  0x....|
982  ** 3 -| ........................                                    .......  ..  0x....|
983  ** 4 -| ........................                                    .......  ..  0x....|
984  ** 5 -| ........................                                    .......  ..  0x....|
985  ** 6 -| ........................                                    .......  ..  0x....|
986  ** 7 -| ........................                                    .......  ..  0x....|
987  ** 8 -| ........................                                    .......  ..  0x....|
988  ** 9 -|---Inactive Drivers--------------------------------------------Dev--------------|
989  ** 10-| ........................                                    .......            |
990  ** 11-| ........................                                    .......            |
991  ** 12-| ........................                                    .......            |
992  ** 13-| ........................                                    .......            |
993  ** 14-| ........................                                    .......            |
994  ** 15-| ........................                                    .......            |
995  ** 16-| ........................                                    .......            |
996  ** 17-|------------------------------------------------------UP-DOWN-------------------|
997  ** 18-| Relevant parameters for the current device                                     |
998  ** 19-|                                                                                |
999  ** 20-|                                                                                |
1000  ** 21-|--------------------------------------------------------------------------------|
1001  ** 22-| Help texts go here                                                             |
1002  ** 23-|                                                                                |
1003  **    +--------------------------------------------------------------------------------+
1004  **
1005  ** Help texts
1006  **
1007  ** On a collapsed comment :
1008  **
1009  ** [Enter] Expand device list      [z]   Expand all lists
1010  ** [TAB]   Change fields           [Q]   Save and Exit
1011  **
1012  ** On an expanded comment :
1013  ** 
1014  ** [Enter] Collapse device list    [Z]   Collapse all lists
1015  ** [TAB]   Change fields           [Q]   Save and Exit
1016  **
1017  ** On a comment with no followers
1018  **
1019  ** 
1020  ** [TAB]   Change fields           [Q]   Save and Exit
1021  **
1022  ** On a device in the active list
1023  **
1024  ** [Enter] Edit device parameters  [DEL] Disable device
1025  ** [TAB]   Change fields           [Q]   Save and Exit             [?] Help
1026  **
1027  ** On a device in the inactive list
1028  **
1029  ** [Enter] Enable device
1030  ** [TAB]   Change fields           [Q]   Save and Exit             [?] Help
1031  **
1032  ** While editing parameters
1033  **
1034  ** <parameter-specific help here>
1035  ** [TAB]   Change fields           [Q]   Save device parameters
1036  **/
1037
1038
1039
1040 /**
1041  **
1042  ** The base-level screen primitives :
1043  **
1044  ** bold()      - enter bold mode               \E[1m     (md)
1045  ** inverse()   - enter inverse mode            \E[7m     (so)
1046  ** normal()    - clear bold/inverse mode       \E[m      (se)
1047  ** clear()     - clear the screen              \E[H\E[J  (ce)
1048  ** move(x,y)   - move the cursor to x,y        \E[y;xH:  (cm)
1049  **/
1050
1051 static void 
1052 bold(void)
1053 {
1054     printf("\033[1m");
1055 }
1056
1057 static void 
1058 inverse(void)
1059 {
1060     printf("\033[7m");
1061 }
1062
1063 static void 
1064 normal(void)
1065 {
1066     printf("\033[m");
1067 }
1068
1069 static void 
1070 clear(void)
1071 {
1072     normal();
1073     printf("\033[H\033[J");
1074 }
1075
1076 static void 
1077 move(int x, int y)
1078 {
1079     printf("\033[%d;%dH",y+1,x+1);
1080 }
1081
1082
1083 /**
1084  **
1085  ** High-level screen primitives :
1086  ** 
1087  ** putxyl(x,y,str,len) - put (len) bytes of (str) at (x,y), supports embedded formatting
1088  ** putxy(x,y,str)      - put (str) at (x,y), supports embedded formatting
1089  ** erase(x,y,w,h)      - clear the box (x,y,w,h)
1090  ** txtbox(x,y,w,y,str) - put (str) in a region at (x,y,w,h)
1091  ** putmsg(str)         - put (str) in the message area
1092  ** puthelp(str)        - put (str) in the upper helpline
1093  ** pad(str,len)        - pad (str) to (len) with spaces
1094  ** drawline(row,detail,list,inverse,*dhelp)
1095  **                     - draws a line for (*list) at (row) onscreen.  If (detail) is 
1096  **                       nonzero, include port, IRQ and maddr, if (inverse) is nonzero,
1097  **                       draw the line in inverse video, and display (*dhelp) on the
1098  **                       helpline.
1099  ** drawlist(row,num,detail,list)
1100  **                     - draw (num) entries from (list) at (row) onscreen, passile (detail)
1101  **                       through to drawline().
1102  ** showparams(dev)     - displays the relevant parameters for (dev) below the lists onscreen.
1103  ** yesno(str)          - displays (str) in the message area, and returns nonzero on 'y' or 'Y'
1104  ** redraw();           - Redraws the entire screen layout, including the 
1105  **                     - two list panels.
1106  **/
1107
1108 /** 
1109  ** putxy
1110  **   writes (str) at x,y onscreen
1111  ** putxyl
1112  **   writes up to (len) of (str) at x,y onscreen.
1113  **
1114  ** Supports embedded formatting :
1115  ** !i - inverse mode.
1116  ** !b - bold mode.
1117  ** !n - normal mode.
1118  **/
1119 static void 
1120 putxyl(int x, int y, char *str, int len)
1121 {
1122     move(x,y);
1123     normal();
1124
1125     while((*str) && (len--))
1126     {
1127         if (*str == '!')                /* format escape? */
1128         {
1129             switch(*(str+1))            /* depending on the next character */
1130             {
1131             case 'i':
1132                 inverse();
1133                 str +=2;                /* skip formatting */
1134                 len++;                  /* doesn't count for length */
1135                 break;
1136                 
1137             case 'b':
1138                 bold();
1139                 str  +=2;               /* skip formatting */
1140                 len++;                  /* doesn't count for length */
1141                 break;
1142
1143             case 'n':
1144                 normal();
1145                 str +=2;                /* skip formatting */
1146                 len++;                  /* doesn't count for length */
1147                 break;
1148                 
1149             default:
1150                 putchar(*str++);        /* not an escape */
1151             }
1152         }else{
1153             putchar(*str++);            /* emit the character */
1154         }
1155     }
1156 }
1157
1158 #define putxy(x,y,str)  putxyl(x,y,str,-1)
1159
1160
1161 /**
1162  ** erase
1163  **
1164  ** Erases the region (x,y,w,h)
1165  **/
1166 static void 
1167 erase(int x, int y, int w, int h)
1168 {
1169     int         i;
1170
1171     normal();
1172     for (i = 0; i < h; i++)
1173         putxyl(x,y++,spaces,w);
1174 }
1175
1176
1177 /** 
1178  ** txtbox
1179  **
1180  ** Writes (str) into the region (x,y,w,h), supports embedded formatting using
1181  ** putxy.  Lines are not wrapped, newlines must be forced with \n.
1182  **/
1183 static void 
1184 txtbox(int x, int y, int w, int h, char *str)
1185 {
1186     int         i = 0;
1187
1188     h--;
1189     while((str[i]) && h)
1190     {
1191         if (str[i] == '\n')                     /* newline */
1192         {
1193             putxyl(x,y,str,(i<w)?i:w);          /* write lesser of i or w */
1194             y++;                                /* move down */
1195             h--;                                /* room for one less */
1196             str += (i+1);                       /* skip first newline */
1197             i = 0;                              /* zero offset */
1198         }else{
1199             i++;                                /* next character */
1200         }
1201     }
1202     if (h)                                      /* end of string, not region */
1203         putxyl(x,y,str,w);
1204 }
1205
1206
1207 /**
1208  ** putmsg
1209  **
1210  ** writes (msg) in the helptext area
1211  **/
1212 static void 
1213 putmsg(char *msg)
1214 {
1215     erase(0,18,80,3);                           /* clear area */
1216     txtbox(0,18,80,3,msg);
1217 }
1218
1219
1220 /**
1221  ** puthelp
1222  **
1223  ** Writes (msg) in the helpline area
1224  **/
1225 static void 
1226 puthelp(char *msg)
1227 {
1228     erase(0,22,80,1);
1229     putxy(0,22,msg);
1230 }
1231
1232
1233 /**
1234  ** masterhelp
1235  **
1236  ** Draws the help message at the bottom of the screen
1237  **/
1238 static void
1239 masterhelp(char *msg)
1240 {
1241     erase(0,23,80,1);
1242     putxy(0,23,msg);
1243 }
1244
1245
1246 /**
1247  ** pad 
1248  **
1249  ** space-pads a (str) to (len) characters
1250  **/
1251 static void 
1252 pad(char *str, int len)
1253 {
1254     int         i;
1255
1256     for (i = 0; str[i]; i++)                    /* find the end of the string */
1257         ;
1258     if (i >= len)                               /* no padding needed */
1259         return;
1260     while(i < len)                              /* pad */
1261         str[i++] = ' ';
1262     str[i] = '\0';
1263 }
1264                                                    
1265
1266 /**
1267  ** drawline
1268  **
1269  ** Displays entry (ofs) of (list) in region at (row) onscreen, optionally displaying
1270  ** the port and IRQ fields if (detail) is nonzero.  If (inverse), in inverse video.
1271  **
1272  ** The text (dhelp) is displayed if the item is a normal device, otherwise
1273  ** help is shown for normal or zoomed comments
1274  **/
1275 static void 
1276 drawline(int row, int detail, DEV_LIST *list, int inverse, char *dhelp)
1277 {
1278     char        lbuf[90],nb[70],db[20],ib[16],pb[16];
1279     
1280     if (list->comment == DEV_DEVICE)
1281     {
1282         nb[0] = ' ';
1283         strncpy(nb+1,list->name,57);
1284     }else{
1285         strncpy(nb,list->name,58);
1286         if ((list->comment == DEV_ZOOMED) && (list->next))
1287             if (list->next->comment == DEV_DEVICE)      /* only mention if there's something hidden */
1288                 strcat(nb,"  (Collapsed)");
1289     }
1290     nb[58] = '\0';
1291     pad(nb,60);
1292     if (list->conflicts)                        /* device in conflict? */
1293     {
1294         if (inverse)
1295         {
1296             strcpy(nb+54," !nCONF!i ");         /* tag conflict, careful of length */
1297         }else{
1298             strcpy(nb+54," !iCONF!n ");         /* tag conflict, careful of length */
1299         }
1300     }
1301     if (list->comment == DEV_DEVICE)
1302     {
1303         sprintf(db,"%s%d",list->dev,list->unit);
1304         pad(db,8);
1305     }else{
1306         strcpy(db,"        ");
1307     }
1308     if ((list->irq > 0) && detail && (list->comment == DEV_DEVICE))
1309     {
1310         sprintf(ib," %d",list->irq);
1311         pad(ib,4);
1312     }else{
1313         strcpy(ib,"    ");
1314     }
1315     if ((list->iobase > 0) && detail && (list->comment == DEV_DEVICE))
1316     {
1317         sprintf(pb,"0x%x",list->iobase);
1318         pad(pb,7);
1319     }else{
1320         strcpy(pb,"       ");
1321     }
1322
1323     sprintf(lbuf,"  %s%s%s%s%s",inverse?"!i":"",nb,db,ib,pb);
1324
1325     putxyl(0,row,lbuf,80);
1326     if (dhelp)
1327     {
1328         switch(list->comment)
1329         {
1330         case DEV_DEVICE:        /* ordinary device */
1331             puthelp(dhelp);
1332             break;
1333         case DEV_COMMENT:
1334             puthelp("");
1335             if (list->next)
1336                 if (list->next->comment == DEV_DEVICE)
1337                     puthelp("  [!bEnter!n] Collapse device list    [!bC!n]    Collapse all lists");
1338             break;
1339         case DEV_ZOOMED:        
1340             puthelp("");
1341             if (list->next)
1342                 if (list->next->comment == DEV_DEVICE)
1343                     puthelp("  [!bEnter!n] Expand device list      [!bX!n]    Expand all lists");
1344             break;
1345         default:
1346             puthelp("  WARNING: This list entry corrupted!");
1347             break;
1348         }
1349     }
1350     move(0,row);                                /* put the cursor somewhere relevant */
1351 }
1352
1353
1354 /**
1355  ** drawlist
1356  **
1357  ** Displays (num) lines of the contents of (list) at (row), optionally displaying the
1358  ** port and IRQ fields as well if (detail) is nonzero
1359  **
1360  ** printf in the kernel is essentially useless, so we do most of the hard work ourselves here.
1361  **/
1362 static void 
1363 drawlist(int row, int num, int detail, DEV_LIST *list)
1364 {
1365     int         ofs;
1366
1367     for(ofs = 0; ofs < num; ofs++)
1368     {
1369         if (list)
1370         {
1371             drawline(row+ofs,detail,list,0,NULL);       /* NULL -> don't draw empty help string */
1372             list = nextent(list);                       /* move down visible list */
1373         }else{
1374             erase(0,row+ofs,80,1);
1375         }
1376     }
1377 }
1378
1379
1380 /**
1381  ** redrawactive
1382  **
1383  ** Redraws the active list 
1384  **/
1385 static void
1386 redrawactive(void)
1387 {
1388     char        cbuf[16];
1389
1390     if (conflicts)
1391     {
1392         sprintf(cbuf,"!i%d conflict%s-",conflicts,(conflicts>1)?"s":"");
1393         putxy(45,0,cbuf);
1394     }else{
1395         putxyl(45,0,lines,16);
1396     }
1397     drawlist(1,8,1,alist);                      /* draw device lists */
1398 }
1399
1400 /**
1401  ** redrawinactive
1402  **
1403  ** Redraws the inactive list 
1404  **/
1405 static void
1406 redrawinactive(void)
1407 {
1408     drawlist(10,7,0,ilist);                     /* draw device lists */
1409 }
1410
1411
1412 /**
1413  ** redraw
1414  **
1415  ** Clear the screen and redraw the entire layout
1416  **/
1417 static void 
1418 redraw(void)
1419 {
1420     clear();
1421     putxy(0,0,lines);
1422     putxy(3,0,"!bActive!n-!bDrivers");
1423     putxy(63,0,"!bDev!n---!bIRQ!n--!bPort");
1424     putxy(0,9,lines);
1425     putxy(3,9,"!bInactive!n-!bDrivers");
1426     putxy(63,9,"!bDev");
1427     putxy(0,17,lines);
1428     putxy(0,21,lines);
1429     masterhelp("  [!bTAB!n]   Change fields           [!bQ!n]   Save and Exit             [!b?!n] Help");
1430
1431     redrawactive();
1432     redrawinactive();
1433 }
1434
1435
1436 /**
1437  ** yesnocancel
1438  **
1439  ** Put (str) in the message area, and return 1 if the user hits 'y' or 'Y',
1440  ** 2 if they hit 'c' or 'C',  or 0 for 'n' or 'N'.
1441  **/
1442 static int
1443 yesnocancel(char *str)
1444 {
1445
1446     putmsg(str);
1447     for(;;)
1448         switch(getchar())
1449         {
1450         case -1:
1451         case 'n':
1452         case 'N':
1453             return(0);
1454             
1455         case 'y':
1456         case 'Y':
1457             return(1);
1458             
1459         case 'c':
1460         case 'C':
1461             return(2);
1462         }
1463 }
1464
1465
1466 /**
1467  ** showparams
1468  **
1469  ** Show device parameters in the region below the lists
1470  **
1471  **     0    5   10   15   20   25   30   35   40   45   50   55   60   67   70   75
1472  **     |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|....
1473  **    +--------------------------------------------------------------------------------+
1474  ** 17-|--------------------------------------------------------------------------------|
1475  ** 18-| Port address : 0x0000     Memory address : 0x00000   Conflict allowed          |
1476  ** 19-| IRQ number   : 00         Memory size    : 0x0000                              |
1477  ** 20-| Flags        : 0x0000     DRQ number     : 00                                  |
1478  ** 21-|--------------------------------------------------------------------------------|
1479  **/
1480 static void 
1481 showparams(DEV_LIST *dev)
1482 {
1483     char        buf[80];
1484
1485     erase(0,18,80,3);                           /* clear area */
1486     if (!dev)
1487         return;
1488     if (dev->comment != DEV_DEVICE)
1489         return;
1490
1491
1492     if (dev->iobase > 0)
1493     {
1494         sprintf(buf,"Port address : 0x%x",dev->iobase);
1495         putxy(1,18,buf);
1496     }
1497             
1498     if (dev->irq > 0)
1499     {
1500         sprintf(buf,"IRQ number   : %d",dev->irq);
1501         putxy(1,19,buf);
1502     }
1503     sprintf(buf,"Flags        : 0x%x",dev->flags);
1504     putxy(1,20,buf);
1505     if (dev->maddr > 0)
1506     {
1507         sprintf(buf,"Memory address : 0x%x",dev->maddr);
1508         putxy(26,18,buf);
1509     }
1510     if (dev->msize > 0)
1511     {
1512         sprintf(buf,"Memory size    : 0x%x",dev->msize);
1513         putxy(26,19,buf);
1514     }
1515
1516     if (dev->drq > 0)
1517     {
1518         sprintf(buf,"DRQ number     : %d",dev->drq);
1519         putxy(26,20,buf);
1520     }
1521 }
1522
1523
1524 /**
1525  ** Editing functions for device parameters
1526  **
1527  ** editval(x,y,width,hex,min,max,val)  - Edit (*val) in a field (width) wide at (x,y)
1528  **                                       onscreen.  Refuse values outsise (min) and (max).
1529  ** editparams(dev)                     - Edit the parameters for (dev)
1530  **/
1531
1532
1533 #define VetRet(code)                                                    \
1534 {                                                                       \
1535     if ((i >= min) && (i <= max))       /* legit? */                    \
1536     {                                                                   \
1537         *val = i;                                                       \
1538         sprintf(buf,hex?"0x%x":"%d",i);                                 \
1539         putxy(hex?x-2:x,y,buf);                                         \
1540         return(code);                   /* all done and exit */         \
1541     }                                                                   \
1542     i = *val;                           /* restore original value */    \
1543     delta = 1;                          /* restore other stuff */       \
1544 }
1545
1546
1547 /**
1548  ** editval
1549  **
1550  ** Edit (*val) at (x,y) in (hex)?hex:decimal mode, allowing values between (min) and (max)
1551  ** in a field (width) wide. (Allow one space)
1552  ** If (ro) is set, we're in "readonly" mode, so disallow edits.
1553  **
1554  ** Return KEY_TAB on \t, KEY_EXIT on 'q'
1555  **/
1556 static int 
1557 editval(int x, int y, int width, int hex, int min, int max, int *val, int ro)
1558 {
1559     int         i = *val;                       /* work with copy of the value */
1560     char        buf[2+11+1],tc[11+1];           /* display buffer, text copy */
1561     int         xp = 0;                         /* cursor offset into text copy */
1562     int         delta = 1;                      /* force redraw first time in */
1563     int         c;
1564     int         extended = 0;                   /* stage counter for extended key sequences */
1565
1566     if (hex)                                    /* we presume there's a leading 0x onscreen */
1567         putxy(x-2,y,"!i0x");                    /* coz there sure is now */
1568         
1569     for (;;)
1570     {
1571         if (delta)                              /* only update if necessary */
1572         {
1573             sprintf(tc,hex?"%x":"%d",i);        /* make a text copy of the value */
1574             sprintf(buf,"!i%s",tc);             /* format for printing */
1575             erase(x,y,width,1);                 /* clear the area */
1576             putxy(x,y,buf);                     /* write */
1577             xp = strlen(tc);                    /* cursor always at end */
1578             move(x+xp,y);                       /* position the cursor */
1579         }
1580
1581         c = getchar();
1582
1583         switch(extended)                        /* escape handling */
1584         {
1585         case 0:
1586             if (c == 0x1b)                      /* esc? */
1587             {
1588                 extended = 1;                   /* flag and spin */
1589                 continue;
1590             }
1591             extended = 0;
1592             break;                              /* nope, drop through */
1593         
1594         case 1:                                 /* there was an escape prefix */
1595             if (c == '[' || c == 'O')           /* second character in sequence */
1596             {
1597                 extended = 2;
1598                 continue;
1599             }
1600             if (c == 0x1b)
1601                 return(KEY_EXIT);               /* double esc exits */
1602             extended = 0;
1603             break;                              /* nup, not a sequence. */
1604
1605         case 2:
1606             extended = 0;
1607             switch(c)                           /* looks like the real McCoy */
1608             {
1609             case 'A':
1610                 VetRet(KEY_UP);                 /* leave if OK */
1611                 continue;
1612             case 'B':
1613                 VetRet(KEY_DOWN);               /* leave if OK */
1614                 continue;
1615             case 'C':
1616                 VetRet(KEY_RIGHT);              /* leave if OK */
1617                 continue;
1618             case 'D':
1619                 VetRet(KEY_LEFT);               /* leave if OK */
1620                 continue;
1621                 
1622             default:
1623                 continue;
1624             }
1625         }
1626     
1627         switch(c)
1628         {
1629         case '\t':                              /* trying to tab off */
1630             VetRet(KEY_TAB);                    /* verify and maybe return */
1631             break;
1632
1633         case -1:
1634         case 'q':
1635         case 'Q':
1636             VetRet(KEY_EXIT);
1637             break;
1638             
1639         case '\b':
1640         case '\177':                            /* BS or DEL */
1641             if (ro)                             /* readonly? */
1642             {
1643                 puthelp(" !iThis value cannot be edited (Press ESC)");
1644                 while(getchar() != 0x1b);       /* wait for key */
1645                 return(KEY_NULL);               /* spin */
1646             }
1647             if (xp)                             /* still something left to delete */
1648             {
1649                 i = (hex ? i/0x10u : i/10);     /* strip last digit */
1650                 delta = 1;                      /* force update */
1651             }
1652             break;
1653
1654         case 588:
1655             VetRet(KEY_UP);
1656             break;
1657
1658         case '\r':
1659         case '\n':
1660         case 596:
1661             VetRet(KEY_DOWN);
1662             break;
1663
1664         case 591:
1665             VetRet(KEY_LEFT);
1666             break;
1667
1668         case 593:
1669             VetRet(KEY_RIGHT);
1670             break;
1671                 
1672         default:
1673             if (ro)                             /* readonly? */
1674             {
1675                 puthelp(" !iThis value cannot be edited (Press ESC)");
1676                 while(getchar() != 0x1b);       /* wait for key */
1677                 return(KEY_NULL);               /* spin */
1678             }
1679             if (xp >= width)                    /* no room for more characters anyway */
1680                 break;
1681             if (hex)
1682             {
1683                 if ((c >= '0') && (c <= '9'))
1684                 {
1685                     i = i*0x10 + (c-'0');       /* update value */
1686                     delta = 1;
1687                     break;
1688                 }
1689                 if ((c >= 'a') && (c <= 'f'))
1690                 {
1691                     i = i*0x10 + (c-'a'+0xa);
1692                     delta = 1;
1693                     break;
1694                 }
1695                 if ((c >= 'A') && (c <= 'F'))
1696                 {
1697                     i = i*0x10 + (c-'A'+0xa);
1698                     delta = 1;
1699                     break;
1700                 }
1701             }else{
1702                 if ((c >= '0') && (c <= '9'))
1703                 {
1704                     i = i*10 + (c-'0');         /* update value */
1705                     delta = 1;                  /* force redraw */
1706                     break;
1707                 }
1708             }
1709             break;
1710         }
1711     }
1712 }
1713
1714
1715 /**
1716  ** editparams
1717  **
1718  ** Edit the parameters for (dev)
1719  **
1720  ** Note that it's _always_ possible to edit the flags, otherwise it might be
1721  ** possible for this to spin in an endless loop...
1722  **     0    5   10   15   20   25   30   35   40   45   50   55   60   67   70   75
1723  **     |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|....
1724  **    +--------------------------------------------------------------------------------+
1725  ** 17-|--------------------------------------------------------------------------------|
1726  ** 18-| Port address : 0x0000     Memory address : 0x00000   Conflict allowed          |
1727  ** 19-| IRQ number   : 00         Memory size    : 0x0000                              |
1728  ** 20-| Flags        : 0x0000     DRQ number     : 00                                  |
1729  ** 21-|--------------------------------------------------------------------------------|
1730  **
1731  ** The "intelligence" in this function that hops around based on the directional
1732  ** returns from editval isn't very smart, and depends on the layout above.
1733  **/
1734 static void 
1735 editparams(DEV_LIST *dev)
1736 {
1737     int         ret;
1738     char        buf[16];                /* needs to fit the device name */
1739
1740     putxy(2,17,"!bParameters!n-!bfor!n-!bdevice!n-");
1741     sprintf(buf,"!b%s",dev->dev);
1742     putxy(24,17,buf);
1743
1744     erase(1,22,80,1);
1745     for (;;)
1746     {
1747     ep_iobase:
1748         if (dev->iobase > 0)
1749         {
1750             puthelp("  IO Port address (Hexadecimal, 0x1-0xffff)");
1751             ret = editval(18,18,5,1,0x1,0xffff,&(dev->iobase),(dev->attrib & FLG_FIXIOBASE));
1752             switch(ret)
1753             {
1754             case KEY_EXIT:
1755                 goto ep_exit;
1756
1757             case KEY_RIGHT:
1758                 if (dev->maddr > 0)
1759                     goto ep_maddr;
1760                 break;
1761
1762             case KEY_TAB:
1763             case KEY_DOWN:
1764                 goto ep_irq;
1765             }
1766             goto ep_iobase;
1767         }
1768     ep_irq:
1769         if (dev->irq > 0)
1770         {
1771             puthelp("  Interrupt number (Decimal, 1-15)");
1772             ret = editval(16,19,3,0,1,15,&(dev->irq),(dev->attrib & FLG_FIXIRQ));
1773             switch(ret)
1774             {
1775             case KEY_EXIT:
1776                 goto ep_exit;
1777
1778             case KEY_RIGHT:
1779                 if (dev->msize > 0)
1780                     goto ep_msize;
1781                 break;
1782
1783             case KEY_UP:
1784                 if (dev->iobase > 0)
1785                     goto ep_iobase;
1786                 break;
1787
1788             case KEY_TAB:
1789             case KEY_DOWN:
1790                 goto ep_flags;
1791             }
1792             goto ep_irq;
1793         }
1794     ep_flags:
1795         puthelp("  Device-specific flag values.");
1796         ret = editval(18,20,8,1,INT_MIN,INT_MAX,&(dev->flags),0);
1797         switch(ret)
1798         {
1799         case KEY_EXIT:
1800             goto ep_exit;
1801
1802         case KEY_RIGHT:
1803             if (dev->drq > 0) 
1804                 goto ep_drq;
1805             break;
1806
1807         case KEY_UP:
1808             if (dev->irq > 0)
1809                 goto ep_irq;
1810             if (dev->iobase > 0)
1811                 goto ep_iobase;
1812             break;
1813
1814         case KEY_DOWN:
1815             if (dev->maddr > 0)
1816                 goto ep_maddr;
1817             if (dev->msize > 0)
1818                 goto ep_msize;
1819             if (dev->drq > 0)
1820                 goto ep_drq;
1821             break;
1822
1823         case KEY_TAB:
1824             goto ep_maddr;
1825         }
1826         goto ep_flags;
1827     ep_maddr:
1828         if (dev->maddr > 0)
1829         {
1830             puthelp("  Device memory start address (Hexadecimal, 0x1-0xfffff)");
1831             ret = editval(45,18,6,1,0x1,0xfffff,&(dev->maddr),(dev->attrib & FLG_FIXMADDR));
1832             switch(ret)
1833             {
1834             case KEY_EXIT:
1835                 goto ep_exit;
1836
1837             case KEY_LEFT:
1838                 if (dev->iobase > 0)
1839                     goto ep_iobase;
1840                 break;
1841
1842             case KEY_UP:
1843                 goto ep_flags;
1844
1845             case KEY_DOWN:
1846                 if (dev->msize > 0)
1847                     goto ep_msize;
1848                 if (dev->drq > 0)
1849                     goto ep_drq;
1850                 break;
1851
1852             case KEY_TAB:
1853                 goto ep_msize;
1854             }
1855             goto ep_maddr;
1856         }
1857     ep_msize:
1858         if (dev->msize > 0)
1859         {
1860             puthelp("  Device memory size (Hexadecimal, 0x1-0x10000)");
1861             ret = editval(45,19,5,1,0x1,0x10000,&(dev->msize),(dev->attrib & FLG_FIXMSIZE));
1862             switch(ret)
1863             {
1864             case KEY_EXIT:
1865                 goto ep_exit;
1866
1867             case KEY_LEFT:
1868                 if (dev->irq > 0)
1869                     goto ep_irq;
1870                 break;
1871
1872             case KEY_UP:
1873                 if (dev->maddr > 0)
1874                     goto ep_maddr;
1875                 goto ep_flags;
1876
1877             case KEY_DOWN:
1878                 if (dev->drq > 0)
1879                     goto ep_drq;
1880                 break;
1881
1882             case KEY_TAB:
1883                 goto ep_drq;
1884             }
1885             goto ep_msize;
1886         }
1887     ep_drq:
1888         if (dev->drq > 0)
1889         {
1890             puthelp("  Device DMA request number (Decimal, 1-7)");
1891             ret = editval(43,20,2,0,1,7,&(dev->drq),(dev->attrib & FLG_FIXDRQ));
1892             switch(ret)
1893             {
1894             case KEY_EXIT:
1895                 goto ep_exit;
1896
1897             case KEY_LEFT:
1898                 goto ep_flags;
1899
1900             case KEY_UP:
1901                 if (dev->msize > 0)
1902                     goto ep_msize;
1903                 if (dev->maddr > 0)
1904                     goto ep_maddr;
1905                 goto ep_flags;
1906
1907             case KEY_TAB:
1908                 goto ep_iobase;
1909             }
1910             goto ep_drq;
1911         }
1912     }
1913     ep_exit:
1914     dev->changed = 1;                                   /* mark as changed */
1915 }
1916
1917 static char *helptext[] =
1918 {
1919     "                Using the UserConfig kernel settings editor",
1920     "                -------------------------------------------",
1921     "",
1922     "VISUAL MODE:",
1923     "",
1924     "- - Layout -",
1925     "",
1926     "The screen displays a list of available drivers, divided into two",
1927     "scrolling lists: Active Drivers, and Inactive Drivers.  Each list is",
1928     "by default collapsed and can be expanded to show all the drivers",
1929     "available in each category.  The parameters for the currently selected",
1930     "driver are shown at the bottom of the screen.",
1931     "",
1932     "- - Moving around -",
1933     "",
1934     "To move in the current list, use the UP and DOWN cursor keys to select",
1935     "an item (the selected item will be highlighted).  If the item is a",
1936     "category name, you may alternatively expand or collapse the list of",
1937     "drivers for that category by pressing [!bENTER!n].  Once the category is",
1938     "expanded, you can select each driver in the same manner and either:",
1939     "",
1940     "  - change its parameters using [!bENTER!n]",
1941     "  - move it to the Inactive list using [!bDEL!n]",
1942     "",
1943     "Use the [!bTAB!n] key to toggle between the Active and Inactive list; if",
1944     "you need to move a driver from the Inactive list back to the Active",
1945     "one, select it in the Inactive list, using [!bTAB!n] to change lists if",
1946     "necessary, and press [!bENTER!n] -- the device will be moved back to",
1947     "its place in the Active list.",
1948     "",
1949     "- - Altering the list/parameters -",
1950     "",
1951     "Any drivers for devices not installed in your system should be moved",
1952     "to the Inactive list, until there are no remaining parameter conflicts",
1953     "between the drivers, as indicated at the top.",
1954     "",
1955     "Once the list of Active drivers only contains entries for the devices",
1956     "present in your system, you can set their parameters (Interrupt, DMA",
1957     "channel, I/O addresses).  To do this, select the driver and press",
1958     "[!bENTER!n]: it is now possible to edit the settings at the",
1959     "bottom of the screen.  Use [!bTAB!n] to change fields, and when you are",
1960     "finished, use [!bQ!n] to return to the list.",
1961     "",
1962     "- - Saving changes -",
1963     "",
1964     "When all settings seem correct, and you wish to proceed with the",
1965     "kernel device probing and boot, press [!bQ!n] -- you will be asked to",
1966     "confirm your choice.",
1967     "",
1968     NULL
1969 };
1970
1971
1972 /**
1973  ** helpscreen
1974  **
1975  ** Displays help text onscreen for people that are confused, using a simple
1976  ** pager.
1977  **/
1978 static void
1979 helpscreen(void) 
1980 {
1981     int         topline = 0;                    /* where we are in the text */
1982     int         line = 0;                       /* last line we displayed */
1983     int         c, delta = 1;
1984     char        prompt[80];
1985
1986     for (;;)                                    /* loop until user quits */
1987     {
1988         /* display help text */
1989         if (delta) 
1990         {
1991             clear();                            /* remove everything else */
1992             for (line = topline; 
1993                  (line < (topline + 24)) && (helptext[line]); 
1994                  line++)
1995                 putxy(0,line-topline,helptext[line]);
1996             delta = 0;
1997         }
1998         
1999         /* prompt */
2000         sprintf(prompt,"!i --%s-- [U]p [D]own [Q]uit !n",helptext[line] ? "MORE" : "END");
2001         putxy(0,24,prompt);
2002         
2003         c = getchar();                          /* so what do they say? */
2004         
2005         switch (c)
2006         {
2007         case 'u':
2008         case 'U':
2009         case 'b':
2010         case 'B':                               /* wired into 'more' users' fingers */
2011             if (topline > 0)                    /* room to go up? */
2012             {
2013                 topline -= 24;
2014                 if (topline < 0)                /* don't go too far */
2015                     topline = 0;
2016                 delta = 1;
2017             }
2018             break;
2019
2020         case 'd':
2021         case 'D':
2022         case ' ':                               /* expected by most people */
2023             if (helptext[line])                 /* maybe more below? */
2024             {
2025                 topline += 24;
2026                 delta = 1;
2027             }
2028             break;
2029             
2030         case 'q':
2031         case 'Q':
2032             redraw();                           /* restore the screen */
2033             return;
2034         }
2035     }
2036 }
2037
2038
2039 /** 
2040  ** High-level control functions
2041  **/
2042
2043
2044 /**
2045  ** dolist
2046  **
2047  ** Handle user movement within (*list) in the region starting at (row) onscreen with
2048  ** (num) lines, starting at (*ofs) offset from row onscreen.
2049  ** Pass (detail) on to drawing routines.
2050  **
2051  ** If the user hits a key other than a cursor key, maybe return a code.
2052  **
2053  ** (*list) points to the device at the top line in the region, (*ofs) is the 
2054  ** position of the highlight within the region.  All routines below
2055  ** this take only a device and an absolute row : use ofsent() to find the 
2056  ** device, and add (*ofs) to (row) to find the absolute row.
2057  **/
2058 static int 
2059 dolist(int row, int num, int detail, int *ofs, DEV_LIST **list, char *dhelp)
2060 {
2061     int         extended = 0;
2062     int         c;
2063     DEV_LIST    *lp;
2064     int         delta = 1;
2065     
2066     for(;;)
2067     {
2068         if (delta)
2069         {
2070             showparams(ofsent(*ofs,*list));                             /* show device parameters */
2071             drawline(row+*ofs,detail,ofsent(*ofs,*list),1,dhelp);       /* highlight current line */
2072             delta = 0;
2073         }
2074
2075         c = getchar();                          /* get a character */
2076         if ((extended == 2) || (c==588) || (c==596))    /* console gives "alternative" codes */
2077         {
2078             extended = 0;                       /* no longer */
2079             switch(c)
2080             {
2081             case 588:                           /* syscons' idea of 'up' */
2082             case 'A':                           /* up */
2083                 if (*ofs)                       /* just a move onscreen */
2084                 {
2085                     drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp);/* unhighlight current line */
2086                     (*ofs)--;                   /* move up */
2087                 }else{
2088                     lp = prevent(*list);        /* can we go up? */
2089                     if (!lp)                    /* no */
2090                         break;
2091                     *list = lp;                 /* yes, move up list */
2092                     drawlist(row,num,detail,*list);
2093                 }
2094                 delta = 1;
2095                 break;
2096
2097             case 596:                           /* dooby-do */
2098             case 'B':                           /* down */
2099                 lp = ofsent(*ofs,*list);        /* get current item */
2100                 if (!nextent(lp))
2101                     break;                      /* nothing more to move to */
2102                 drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp);   /* unhighlight current line */
2103                 if (*ofs < (num-1))             /* room to move onscreen? */
2104                 {
2105                     (*ofs)++;               
2106                 }else{
2107                     *list = nextent(*list);     /* scroll region down */
2108                     drawlist(row,num,detail,*list);
2109                 }               
2110                 delta = 1;
2111                 break;
2112             }
2113         }else{
2114             switch(c)
2115             {
2116             case '\033':
2117                 extended=1;
2118                 break;
2119                     
2120             case '[':                           /* cheat : always preceeds cursor move */
2121             case 'O':                           /* ANSI application key mode */
2122                 if (extended==1)
2123                     extended=2;
2124                 else
2125                     extended=0;
2126                 break;
2127                 
2128             case 'Q':
2129             case 'q':
2130                 return(KEY_EXIT);               /* user requests exit */
2131
2132             case '\r':                          
2133             case '\n':
2134                 return(KEY_DO);                 /* "do" something */
2135
2136             case '\b':
2137             case '\177':
2138             case 599:
2139                 return(KEY_DEL);                /* "delete" response */
2140
2141             case 'X':
2142             case 'x':
2143                 return(KEY_UNZOOM);             /* expand everything */
2144                 
2145             case 'C':
2146             case 'c':
2147                 return(KEY_ZOOM);               /* collapse everything */
2148
2149             case '\t':
2150                 drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp);   /* unhighlight current line */
2151                 return(KEY_TAB);                                /* "move" response */
2152                 
2153             case '\014':                        /* ^L, redraw */
2154                 return(KEY_REDRAW);
2155                 
2156             case '?':                           /* helptext */
2157                 return(KEY_HELP);
2158                 
2159             }
2160         }
2161     }           
2162 }
2163
2164
2165 /**
2166  ** visuserconfig
2167  ** 
2168  ** Do the fullscreen config thang
2169  **/
2170 static int
2171 visuserconfig(void)
2172 {
2173     int actofs = 0, inactofs = 0, mode = 0, ret = -1, i;
2174     DEV_LIST    *dp;
2175     
2176     initlist(&active);
2177     initlist(&inactive);
2178     alist = active;
2179     ilist = inactive;
2180
2181     getdevs();
2182
2183     conflicts = findconflict(active);           /* find conflicts in the active list only */
2184
2185     redraw();
2186
2187     for(;;)
2188     {
2189         switch(mode)
2190         {
2191         case 0:                                 /* active devices */
2192             ret = dolist(1,8,1,&actofs,&alist,
2193                          "  [!bEnter!n] Edit device parameters  [!bDEL!n] Disable device");
2194             switch(ret)
2195             {
2196             case KEY_TAB:
2197                 mode = 1;                       /* swap lists */
2198                 break;
2199
2200             case KEY_REDRAW:
2201                 redraw();
2202                 break;
2203
2204             case KEY_ZOOM:
2205                 alist = active;
2206                 actofs = 0;
2207                 expandlist(active);
2208                 redrawactive();
2209                 break;
2210
2211             case KEY_UNZOOM:
2212                 alist = active;
2213                 actofs = 0;
2214                 collapselist(active);
2215                 redrawactive();
2216                 break;
2217
2218             case KEY_DEL:
2219                 dp = ofsent(actofs,alist);      /* get current device */
2220                 if (dp)                         /* paranoia... */
2221                 {
2222                     if (dp->attrib & FLG_MANDATORY)     /* can't be deleted */
2223                         break;
2224                     if (dp == alist)            /* moving top item on list? */
2225                     {
2226                         if (dp->next)
2227                         {
2228                             alist = dp->next;   /* point list to non-moving item */
2229                         }else{
2230                             alist = dp->prev;   /* end of list, go back instead */
2231                         }
2232                     }else{
2233                         if (!dp->next)          /* moving last item on list? */
2234                             actofs--;
2235                     }
2236                     dp->conflicts = 0;          /* no conflicts on the inactive list */
2237                     movedev(dp,inactive);       /* shift to inactive list */
2238                     conflicts = findconflict(active);   /* update conflict tags */
2239                     dp->changed = 1;
2240                     redrawactive();                     /* redraw */
2241                     redrawinactive();
2242                 }
2243                 break;
2244                 
2245             case KEY_DO:                        /* edit device parameters */
2246                 dp = ofsent(actofs,alist);      /* get current device */
2247                 if (dp)                         /* paranoia... */
2248                 {
2249                     if (dp->comment == DEV_DEVICE)      /* can't edit comments, zoom? */
2250                     {
2251                         masterhelp("  [!bTAB!n]   Change fields           [!bQ!n]   Save device parameters");
2252                         editparams(dp);
2253                         masterhelp("  [!bTAB!n]   Change fields           [!bQ!n]   Save and Exit             [!b?!n] Help");
2254                         putxy(0,17,lines);
2255                         conflicts = findconflict(active);       /* update conflict tags */
2256                     }else{                              /* DO on comment = zoom */
2257                         switch(dp->comment)             /* Depends on current state */
2258                         {
2259                         case DEV_COMMENT:               /* not currently zoomed */
2260                             dp->comment = DEV_ZOOMED;
2261                             break;
2262
2263                         case DEV_ZOOMED:
2264                             dp->comment = DEV_COMMENT;
2265                             break;
2266                         }
2267                     }
2268                     redrawactive();
2269                 }
2270                 break;
2271             }
2272             break;
2273
2274         case 1:                                 /* inactive devices */
2275             ret = dolist(10,7,0,&inactofs,&ilist,
2276                          "  [!bEnter!n] Enable device                                   ");
2277             switch(ret)
2278             {
2279             case KEY_TAB:
2280                 mode = 0;
2281                 break;
2282
2283             case KEY_REDRAW:
2284                 redraw();
2285                 break;
2286
2287             case KEY_ZOOM:
2288                 ilist = inactive;
2289                 inactofs = 0;
2290                 expandlist(inactive);
2291                 redrawinactive();
2292                 break;
2293
2294             case KEY_UNZOOM:
2295                 ilist = inactive;
2296                 inactofs = 0;
2297                 collapselist(inactive);
2298                 redrawinactive();
2299                 break;
2300
2301             case KEY_DO:
2302                 dp = ofsent(inactofs,ilist);    /* get current device */
2303                 if (dp)                         /* paranoia... */
2304                 {
2305                     if (dp->comment == DEV_DEVICE)      /* can't move comments, zoom? */
2306                     {
2307                         if (dp == ilist)                /* moving top of list? */
2308                         {
2309                             if (dp->next)
2310                             {
2311                                 ilist = dp->next;       /* point list to non-moving item */
2312                             }else{
2313                                 ilist = dp->prev;       /* can't go down, go up instead */
2314                             }
2315                         }else{
2316                             if (!dp->next)              /* last entry on list? */
2317                                 inactofs--;             /* shift cursor up one */
2318                         }
2319
2320                         movedev(dp,active);             /* shift to active list */
2321                         conflicts = findconflict(active);       /* update conflict tags */
2322                         dp->changed = 1;
2323                         alist = dp;                     /* put at top and current */
2324                         actofs = 0;
2325                         while(dp->comment == DEV_DEVICE)
2326                             dp = dp->prev;              /* forcibly unzoom section */
2327                         dp ->comment = DEV_COMMENT;
2328                         mode = 0;                       /* and swap modes to follow it */
2329
2330                     }else{                              /* DO on comment = zoom */
2331                         switch(dp->comment)             /* Depends on current state */
2332                         {
2333                         case DEV_COMMENT:               /* not currently zoomed */
2334                             dp->comment = DEV_ZOOMED;
2335                             break;
2336
2337                         case DEV_ZOOMED:
2338                             dp->comment = DEV_COMMENT;
2339                             break;
2340                         }
2341                     }
2342                     redrawactive();                     /* redraw */
2343                     redrawinactive();
2344                 }
2345                 break;
2346
2347             default:                            /* nothing else relevant here */
2348                 break;
2349             }
2350             break;
2351         default:
2352             mode = 0;                           /* shouldn't happen... */
2353         }
2354
2355         /* handle returns that are the same for both modes */
2356         switch (ret) {
2357         case KEY_HELP:
2358             helpscreen();
2359             break;
2360             
2361         case KEY_EXIT:
2362             i = yesnocancel(" Save these parameters before exiting? ([!bY!n]es/[!bN!n]o/[!bC!n]ancel) ");
2363             switch(i)
2364             {
2365             case 2:                             /* cancel */
2366                 redraw();
2367                 break;
2368                 
2369             case 1:                             /* save and exit */
2370                 savelist(active,1);
2371                 savelist(inactive,0);
2372
2373             case 0:                             /* exit */
2374                 nukelist(active);               /* clean up after ourselves */
2375                 nukelist(inactive);
2376                 normal();
2377                 clear();
2378                 return(1);
2379             }
2380             break;
2381         }
2382     }
2383 }
2384 #endif /* VISUAL_USERCONFIG */
2385
2386 /*
2387  * Copyright (c) 1991 Regents of the University of California.
2388  * All rights reserved.
2389  * Copyright (c) 1994 Jordan K. Hubbard
2390  * All rights reserved.
2391  * Copyright (c) 1994 David Greenman
2392  * All rights reserved.
2393  *
2394  * Many additional changes by Bruce Evans
2395  *
2396  * This code is derived from software contributed by the
2397  * University of California Berkeley, Jordan K. Hubbard,
2398  * David Greenman and Bruce Evans.
2399  *
2400  * Redistribution and use in source and binary forms, with or without
2401  * modification, are permitted provided that the following conditions
2402  * are met:
2403  * 1. Redistributions of source code must retain the above copyright
2404  *    notice, this list of conditions and the following disclaimer.
2405  * 2. Redistributions in binary form must reproduce the above copyright
2406  *    notice, this list of conditions and the following disclaimer in the
2407  *    documentation and/or other materials provided with the distribution.
2408  * 3. All advertising materials mentioning features or use of this software
2409  *    must display the following acknowledgement:
2410  *      This product includes software developed by the University of
2411  *      California, Berkeley and its contributors.
2412  * 4. Neither the name of the University nor the names of its contributors
2413  *    may be used to endorse or promote products derived from this software
2414  *    without specific prior written permission.
2415  *
2416  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2417  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2418  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2419  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2420  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2421  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2422  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2423  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2424  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2425  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2426  * SUCH DAMAGE.
2427  *
2428  * $FreeBSD: src/sys/i386/i386/userconfig.c,v 1.175.2.10 2002/10/05 18:31:48 scottl Exp $
2429  */
2430
2431 #include "use_scbus.h"
2432
2433 #define PARM_DEVSPEC    0x1
2434 #define PARM_INT        0x2
2435 #define PARM_ADDR       0x3
2436 #define PARM_STRING     0x4
2437
2438 typedef struct _cmdparm {
2439     int type;
2440     union {
2441         struct uc_device *dparm;
2442         int iparm;
2443         union {
2444                 void *aparm;
2445                 const char *sparm;
2446         } u;
2447     } parm;
2448 } CmdParm;
2449
2450 typedef int (*CmdFunc)(CmdParm *);
2451
2452 typedef struct _cmd {
2453     char *name;
2454     CmdFunc handler;
2455     CmdParm *parms;
2456 } Cmd;
2457
2458
2459 #if 0
2460 static void lsscsi(void);
2461 static int list_scsi(CmdParm *);
2462 #endif
2463
2464 static int lsdevtab(struct uc_device *);
2465 static struct uc_device *find_device(char *, int);
2466 static struct uc_device *search_devtable(struct uc_device *, char *, int);
2467 static void cngets(char *, int);
2468 static Cmd *parse_cmd(char *);
2469 static int parse_args(const char *, CmdParm *);
2470 static int save_dev(struct uc_device *);
2471
2472 static int list_devices(CmdParm *);
2473 static int set_device_ioaddr(CmdParm *);
2474 static int set_device_irq(CmdParm *);
2475 static int set_device_drq(CmdParm *);
2476 static int set_device_iosize(CmdParm *);
2477 static int set_device_mem(CmdParm *);
2478 static int set_device_flags(CmdParm *);
2479 static int set_device_enable(CmdParm *);
2480 static int set_device_disable(CmdParm *);
2481 static int quitfunc(CmdParm *);
2482 static int helpfunc(CmdParm *);
2483 static int introfunc(CmdParm *);
2484
2485 #if NPNP > 0
2486 static int lspnp(void);
2487 static int set_pnp_parms(CmdParm *);
2488 #endif
2489
2490 static int lineno;
2491
2492 #include "use_eisa.h"
2493
2494 #if NEISA > 0
2495
2496 #include <bus/eisa/eisaconf.h>
2497
2498 static int set_num_eisa_slots(CmdParm *);
2499
2500 #endif /* NEISA > 0 */
2501
2502 static CmdParm addr_parms[] = {
2503     { PARM_DEVSPEC, {} },
2504     { PARM_ADDR, {} },
2505     { -1, {} },
2506 };
2507
2508 static CmdParm int_parms[] = {
2509     { PARM_DEVSPEC, {} },
2510     { PARM_INT, {} },
2511     { -1, {} },
2512 };
2513
2514 static CmdParm dev_parms[] = {
2515     { PARM_DEVSPEC, {} },
2516     { -1, {} },
2517 };
2518
2519 #if NPNP > 0
2520 static CmdParm string_arg[] = {
2521     { PARM_STRING, {} },
2522     { -1, {} },
2523 };
2524 #endif
2525
2526 #if NEISA > 0
2527 static CmdParm int_arg[] = {
2528     { PARM_INT, {} },
2529     { -1, {} },
2530 };
2531 #endif /* NEISA > 0 */
2532
2533 static Cmd CmdList[] = {
2534     { "?",      helpfunc,               NULL },         /* ? (help)     */
2535     { "di",     set_device_disable,     dev_parms },    /* disable dev  */
2536     { "dr",     set_device_drq,         int_parms },    /* drq dev #    */
2537 #if NEISA > 0
2538     { "ei",     set_num_eisa_slots,     int_arg },      /* # EISA slots */
2539 #endif /* NEISA > 0 */
2540     { "en",     set_device_enable,      dev_parms },    /* enable dev   */
2541     { "ex",     quitfunc,               NULL },         /* exit (quit)  */
2542     { "f",      set_device_flags,       int_parms },    /* flags dev mask */
2543     { "h",      helpfunc,               NULL },         /* help         */
2544     { "intro",  introfunc,              NULL },         /* intro screen */
2545     { "iom",    set_device_mem,         addr_parms },   /* iomem dev addr */
2546     { "ios",    set_device_iosize,      int_parms },    /* iosize dev size */
2547     { "ir",     set_device_irq,         int_parms },    /* irq dev #    */
2548     { "l",      list_devices,           NULL },         /* ls, list     */
2549 #if NPNP > 0
2550     { "pn",     set_pnp_parms,          string_arg },   /* pnp ... */
2551 #endif
2552     { "po",     set_device_ioaddr,      int_parms },    /* port dev addr */
2553     { "res",    (CmdFunc)cpu_reset,     NULL },         /* reset CPU    */
2554     { "q",      quitfunc,               NULL },         /* quit         */
2555 #if 0
2556     { "s",      list_scsi,              NULL },         /* scsi */
2557 #endif
2558 #ifdef VISUAL_USERCONFIG
2559     { "v",      (CmdFunc)visuserconfig, NULL },         /* visual mode */
2560 #endif
2561     { NULL,     NULL,                   NULL },
2562 };
2563
2564 void
2565 userconfig(void)
2566 {
2567     static char banner = 1;
2568     char input[80];
2569     int rval;
2570     Cmd *cmd;
2571
2572     load_devtab();
2573     init_config_script();
2574     while (1) {
2575
2576         /* Only display signon banner if we are about to go interactive */
2577         if (!has_config_script()) {
2578             if (!(boothowto & RB_CONFIG))
2579 #ifdef INTRO_USERCONFIG
2580                 banner = 0;
2581 #else
2582                 return;
2583 #endif
2584             if (banner) {
2585                 banner = 0;
2586                 printf("FreeBSD Kernel Configuration Utility - Version 1.2\n"
2587                        " Type \"help\" for help" 
2588 #ifdef VISUAL_USERCONFIG
2589                        " or \"visual\" to go to the visual\n"
2590                        " configuration interface (requires MGA/VGA display or\n"
2591                        " serial terminal capable of displaying ANSI graphics)"
2592 #endif
2593                        ".\n");
2594             }
2595         }
2596
2597         printf("config> ");
2598         cngets(input, 80);
2599         if (input[0] == '\0')
2600             continue;
2601         cmd = parse_cmd(input);
2602         if (!cmd) {
2603             printf("Invalid command or syntax.  Type `?' for help.\n");
2604             continue;
2605         }
2606         rval = (*cmd->handler)(cmd->parms);
2607         if (rval) {
2608             free_devtab();
2609             return;
2610         }
2611     }
2612 }
2613
2614 static Cmd *
2615 parse_cmd(char *cmd)
2616 {
2617     Cmd *cp;
2618
2619     for (cp = CmdList; cp->name; cp++) {
2620         int len = strlen(cp->name);
2621
2622         if (!strncmp(cp->name, cmd, len)) {
2623             while (*cmd && *cmd != ' ' && *cmd != '\t')
2624                 ++cmd;
2625             if (parse_args(cmd, cp->parms))
2626                 return NULL;
2627             else
2628                 return cp;
2629         }
2630     }
2631     return NULL;
2632 }
2633
2634 static int
2635 parse_args(const char *cmd, CmdParm *parms)
2636 {
2637     while (1) {
2638         char *ptr;
2639
2640         if (*cmd == ' ' || *cmd == '\t') {
2641             ++cmd;
2642             continue;
2643         }
2644         if (parms == NULL || parms->type == -1) {
2645                 if (*cmd == '\0')
2646                         return 0;
2647                 printf("Extra arg(s): %s\n", cmd);
2648                 return 1;
2649         }
2650         if (parms->type == PARM_DEVSPEC) {
2651             int i = 0;
2652             char devname[64];
2653             int unit = 0;
2654
2655             while (*cmd && !(*cmd == ' ' || *cmd == '\t' ||
2656               (*cmd >= '0' && *cmd <= '9')))
2657                 devname[i++] = *(cmd++);
2658             devname[i] = '\0';
2659             if (*cmd >= '0' && *cmd <= '9') {
2660                 unit = strtoul(cmd, &ptr, 10);
2661                 if (cmd == ptr) {
2662                     printf("Invalid device number\n");
2663                     /* XXX should print invalid token here and elsewhere. */
2664                     return 1;
2665                 }
2666                 /* XXX else should require end of token. */
2667                 cmd = ptr;
2668             }
2669             if ((parms->parm.dparm = find_device(devname, unit)) == NULL) {
2670                 printf("No such device: %s%d\n", devname, unit);
2671                 return 1;
2672             }
2673             ++parms;
2674             continue;
2675         }
2676         if (parms->type == PARM_INT) {
2677             parms->parm.iparm = strtoul(cmd, &ptr, 0);
2678             if (cmd == ptr) {
2679                 printf("Invalid numeric argument\n");
2680                 return 1;
2681             }
2682             cmd = ptr;
2683             ++parms;
2684             continue;
2685         }
2686         if (parms->type == PARM_ADDR) {
2687             parms->parm.u.aparm = (void *)(uintptr_t)strtoul(cmd, &ptr, 0);
2688             if (cmd == ptr) {
2689                 printf("Invalid address argument\n");
2690                 return 1;
2691             }
2692             cmd = ptr;
2693             ++parms;
2694             continue;
2695         }
2696         if (parms->type == PARM_STRING) {
2697             parms->parm.u.sparm = cmd;
2698             return 0;
2699         }
2700     }
2701     return 0;
2702 }
2703
2704 static int
2705 list_devices(CmdParm *parms)
2706 {
2707     lineno = 0;
2708     if (lsdevtab(uc_devtab)) return 0;
2709 #if NPNP > 0
2710     if (lspnp()) return 0;
2711 #endif
2712 #if NEISA > 0
2713     printf("\nNumber of EISA slots to probe: %d\n", num_eisa_slots);
2714 #endif /* NEISA > 0 */
2715     return 0;
2716 }
2717
2718 static int
2719 set_device_ioaddr(CmdParm *parms)
2720 {
2721     parms[0].parm.dparm->id_iobase = parms[1].parm.iparm;
2722     save_dev(parms[0].parm.dparm);
2723     return 0;
2724 }
2725
2726 static int
2727 set_device_irq(CmdParm *parms)
2728 {
2729     unsigned irq;
2730
2731     irq = parms[1].parm.iparm;
2732     if (irq == 2) {
2733         printf("Warning: Remapping IRQ 2 to IRQ 9\n");
2734         irq = 9;
2735     }
2736     else if (irq != -1 && irq > 15) {
2737         printf("An IRQ > 15 would be invalid.\n");
2738         return 0;
2739     }
2740     parms[0].parm.dparm->id_irq = (irq < 16 ? 1 << irq : 0);
2741     save_dev(parms[0].parm.dparm);
2742     return 0;
2743 }
2744
2745 static int
2746 set_device_drq(CmdParm *parms)
2747 {
2748     unsigned drq;
2749
2750     /*
2751      * The bounds checking is just to ensure that the value can be printed
2752      * in 5 characters.  32768 gets converted to -32768 and doesn't fit.
2753      */
2754     drq = parms[1].parm.iparm;
2755     parms[0].parm.dparm->id_drq = (drq < 32768 ? drq : -1);
2756     save_dev(parms[0].parm.dparm);
2757     return 0;
2758 }
2759
2760 static int
2761 set_device_iosize(CmdParm *parms)
2762 {
2763     parms[0].parm.dparm->id_msize = parms[1].parm.iparm;
2764     save_dev(parms[0].parm.dparm);
2765     return 0;
2766 }
2767
2768 static int
2769 set_device_mem(CmdParm *parms)
2770 {
2771     parms[0].parm.dparm->id_maddr = parms[1].parm.u.aparm;
2772     save_dev(parms[0].parm.dparm);
2773     return 0;
2774 }
2775
2776 static int
2777 set_device_flags(CmdParm *parms)
2778 {
2779     parms[0].parm.dparm->id_flags = parms[1].parm.iparm;
2780     save_dev(parms[0].parm.dparm);
2781     return 0;
2782 }
2783
2784 static int
2785 set_device_enable(CmdParm *parms)
2786 {
2787     parms[0].parm.dparm->id_enabled = TRUE;
2788     save_dev(parms[0].parm.dparm);
2789     return 0;
2790 }
2791
2792 static int
2793 set_device_disable(CmdParm *parms)
2794 {
2795     parms[0].parm.dparm->id_enabled = FALSE;
2796     save_dev(parms[0].parm.dparm);
2797     return 0;
2798 }
2799
2800 #if NPNP > 0
2801
2802 static int
2803 sysctl_machdep_uc_pnplist(SYSCTL_HANDLER_ARGS)
2804 {
2805         int error=0;
2806
2807         if(!req->oldptr) {
2808                 /* Only sizing */
2809                 return(SYSCTL_OUT(req,0,sizeof(struct pnp_cinfo)*MAX_PNP_LDN));
2810         } else {
2811                 /*
2812                  * Output the pnp_ldn_overrides[] table.
2813                  */
2814                 error=sysctl_handle_opaque(oidp,&pnp_ldn_overrides,
2815                         sizeof(struct pnp_cinfo)*MAX_PNP_LDN,req);
2816                 if(error) return(error);
2817                 return(0);
2818         }
2819 }
2820
2821 SYSCTL_PROC( _machdep, OID_AUTO, uc_pnplist, CTLFLAG_RD,
2822         0, 0, sysctl_machdep_uc_pnplist, "A",
2823         "List of PnP overrides changed in UserConfig");
2824
2825 /*
2826  * this function sets the kernel table to override bios PnP
2827  * configuration.
2828  */
2829 static int      
2830 set_pnp_parms(CmdParm *parms)      
2831 {   
2832     u_long idx, val, ldn, csn;
2833     int i;
2834     char *q;
2835     const char *p = parms[0].parm.u.sparm;
2836     struct pnp_cinfo d;
2837
2838     csn=strtoul(p,&q, 0);
2839     ldn=strtoul(q,&q, 0);
2840     for (p=q; *p && (*p==' ' || *p=='\t'); p++) ;
2841     if (csn < 1 || csn > MAX_PNP_CARDS || ldn >= MAX_PNP_LDN) {
2842         printf("bad csn/ldn %ld:%ld\n", csn, ldn);
2843         return 0;
2844     }
2845     for (i=0; i < MAX_PNP_LDN; i++) {
2846         if (pnp_ldn_overrides[i].csn == csn &&
2847             pnp_ldn_overrides[i].ldn == ldn)
2848                 break;
2849     }
2850     if (i==MAX_PNP_LDN) {
2851         for (i=0; i < MAX_PNP_LDN; i++) {
2852             if (pnp_ldn_overrides[i].csn <1 ||
2853                  pnp_ldn_overrides[i].csn > MAX_PNP_CARDS)
2854                  break;
2855         }
2856     }
2857     if (i==MAX_PNP_LDN) {
2858         printf("sorry, no PnP entries available, try delete one\n");
2859         return 0 ;
2860     }
2861     d = pnp_ldn_overrides[i] ;
2862     d.csn = csn;
2863     d.ldn = ldn ;
2864     while (*p) {
2865         idx = 0;
2866         val = 0;
2867         if (!strncmp(p,"irq",3)) {
2868             idx=strtoul(p+3,&q, 0);
2869             val=strtoul(q,&q, 0);
2870             if (idx >=0 && idx < 2) d.irq[idx] = val;
2871         } else if (!strncmp(p,"flags",5)) {
2872             idx=strtoul(p+5,&q, 0);
2873             d.flags = idx;
2874         } else if (!strncmp(p,"drq",3)) {
2875             idx=strtoul(p+3,&q, 0);
2876             val=strtoul(q,&q, 0);
2877             if (idx >=0 && idx < 2) d.drq[idx] = val;
2878         } else if (!strncmp(p,"port",4)) {
2879             idx=strtoul(p+4,&q, 0);
2880             val=strtoul(q,&q, 0);
2881             if (idx >=0 && idx < 8) d.port[idx] = val;
2882         } else if (!strncmp(p,"mem",3)) {
2883             idx=strtoul(p+3,&q, 0);
2884             val=strtoul(q,&q, 0);
2885             if (idx >=0 && idx < 4) d.mem[idx].base = val;
2886         } else if (!strncmp(p,"bios",4)) {
2887             q = p+ 4;
2888             d.override = 0 ;
2889         } else if (!strncmp(p,"os",2)) {
2890             q = p+2 ;
2891             d.override = 1 ;
2892         } else if (!strncmp(p,"disable",7)) {
2893             q = p+7 ;
2894             d.enable = 0 ;
2895         } else if (!strncmp(p,"enable",6)) {
2896             q = p+6;
2897             d.enable = 1 ;
2898         } else if (!strncmp(p,"delete",6)) {
2899             bzero(&pnp_ldn_overrides[i], sizeof (pnp_ldn_overrides[i]));
2900             if (i==0) pnp_ldn_overrides[i].csn = 255;/* not reinit */
2901             return 0;
2902         } else {
2903             printf("unknown command <%s>\n", p);
2904             break;
2905         }
2906         for (p=q; *p && (*p==' ' || *p=='\t'); p++) ;
2907     }
2908     pnp_ldn_overrides[i] = d ;
2909     return 0; 
2910 }
2911 #endif /* NPNP */
2912
2913 #if NEISA > 0
2914 static int
2915 set_num_eisa_slots(CmdParm *parms)
2916 {
2917     int num_slots;
2918
2919     num_slots = parms[0].parm.iparm;
2920     num_eisa_slots = (num_slots <= 16 ? num_slots : 10);
2921     return 0;
2922 }
2923 #endif /* NEISA > 0 */
2924
2925 static int
2926 quitfunc(CmdParm *parms)
2927 {
2928     /*
2929      * If kernel config supplied, and we are parsing it, and -c also supplied,
2930      * ignore a quit command,  This provides a safety mechanism to allow
2931      * recovery from a damaged/buggy kernel config.
2932      */
2933     if ((boothowto & RB_CONFIG) && userconfig_boot_parsing)
2934         return 0;
2935     return 1;
2936 }
2937
2938 static int
2939 helpfunc(CmdParm *parms)
2940 {
2941     printf(
2942     "Command\t\t\tDescription\n"
2943     "-------\t\t\t-----------\n"
2944     "ls\t\t\tList currently configured devices\n"
2945     "port <devname> <addr>\tSet device port (i/o address)\n"
2946     "irq <devname> <number>\tSet device irq\n"
2947     "drq <devname> <number>\tSet device drq\n"
2948     "iomem <devname> <addr>\tSet device maddr (memory address)\n"
2949     "iosize <devname> <size>\tSet device memory size\n"
2950     "flags <devname> <mask>\tSet device flags\n"
2951     "enable <devname>\tEnable device\n"
2952     "disable <devname>\tDisable device (will not be probed)\n");
2953 #if NPNP > 0
2954     printf(
2955     "pnp <csn> <ldn> [enable|disable]\tenable/disable device\n"
2956     "pnp <csn> <ldn> [os|bios]\tset parameters using FreeBSD or BIOS\n"
2957     "pnp <csn> <ldn> [portX <addr>]\tset addr for port X (0..7)\n"
2958     "pnp <csn> <ldn> [memX <maddr>]\tset addr for memory range X (0..3)\n"
2959     "pnp <csn> <ldn> [irqX <number>]\tset irq X (0..1) to number, 0=unused\n"
2960     "pnp <csn> <ldn> [drqX <number>]\tset drq X (0..1) to number, 4=unused\n");
2961 #endif
2962 #if NEISA > 0
2963     printf("eisa <number>\t\tSet the number of EISA slots to probe\n");
2964 #endif /* NEISA > 0 */
2965     printf(
2966     "quit\t\t\tExit this configuration utility\n"
2967     "reset\t\t\tReset CPU\n");
2968 #ifdef VISUAL_USERCONFIG
2969     printf("visual\t\t\tGo to fullscreen mode.\n");
2970 #endif
2971     printf(
2972     "help\t\t\tThis message\n\n"
2973     "Commands may be abbreviated to a unique prefix\n");
2974     return 0;
2975 }
2976
2977 #if defined (VISUAL_USERCONFIG)
2978 static void
2979 center(int y, char *str)
2980 {
2981     putxy((80 - strlen(str)) / 2, y, str);
2982 }
2983 #endif
2984
2985 static int
2986 introfunc(CmdParm *parms)
2987 {
2988 #if defined (VISUAL_USERCONFIG)
2989     int curr_item, first_time, extended = 0;
2990     static char *choices[] = {
2991         " Skip kernel configuration and continue with installation ",
2992         " Start kernel configuration in full-screen visual mode    ",
2993         " Start kernel configuration in CLI mode                   ",
2994     };
2995
2996     clear();
2997     center(2, "!bKernel Configuration Menu!n");
2998
2999     curr_item = 0;
3000     first_time = 1;
3001     while (1) {
3002         char tmp[80];
3003         int c, i;
3004
3005         if (!extended) { 
3006             for (i = 0; i < 3; i++) {
3007                 tmp[0] = '\0';
3008                 if (curr_item == i)
3009                     strcpy(tmp, "!i");
3010                 strcat(tmp, choices[i]);
3011                 if (curr_item == i)
3012                     strcat(tmp, "!n");
3013                 putxy(10, 5 + i, tmp);
3014             }
3015
3016             if (first_time) {
3017                 putxy(2, 10, "Here you have the chance to go into kernel configuration mode, making");
3018                 putxy(2, 11, "any changes which may be necessary to properly adjust the kernel to");
3019                 putxy(2, 12, "match your hardware configuration.");
3020                 putxy(2, 14, "If you are installing FreeBSD for the first time, select Visual Mode");
3021                 putxy(2, 15, "(press Down-Arrow then ENTER).");
3022                 putxy(2, 17, "If you need to do more specialized kernel configuration and are an");
3023                 putxy(2, 18, "experienced FreeBSD user, select CLI mode.");
3024                 putxy(2, 20, "If you are !icertain!n that you do not need to configure your kernel");
3025                 putxy(2, 21, "then simply press ENTER or Q now.");
3026                 first_time = 0;
3027             }
3028             
3029             move(0, 0); /* move the cursor out of the way */
3030         }
3031         c = getchar();
3032         if ((extended == 2) || (c == 588) || (c == 596)) {      /* console gives "alternative" codes */
3033             extended = 0;               /* no longer */
3034             switch (c) {
3035             case 588:
3036             case 'A':                           /* up */
3037                 if (curr_item > 0)
3038                     --curr_item;
3039                 break;
3040
3041             case 596:
3042             case 'B':                           /* down */
3043                 if (curr_item < 2)
3044                     ++curr_item;
3045                 break;
3046             }
3047         }
3048         else {
3049             switch(c) {
3050             case '\033':
3051                 extended = 1;
3052                 break;
3053                     
3054             case '[':                           /* cheat : always preceeds cursor move */
3055             case 'O':                           /* ANSI application key mode */
3056                 if (extended == 1)
3057                     extended = 2;
3058                 else
3059                     extended = 0;
3060                 break;
3061                 
3062             case -1:
3063             case 'Q':
3064             case 'q':
3065                 clear();
3066                 return 1;       /* user requests exit */
3067
3068             case '1':                           /* select an item */
3069             case 'S':
3070             case 's':
3071                 curr_item = 0;
3072                 break;
3073             case '2':
3074             case 'V':
3075             case 'v':
3076                 curr_item = 1;
3077                 break;
3078             case '3':
3079             case 'C':
3080             case 'c':
3081                 curr_item = 2;
3082                 break;
3083
3084             case 'U':                           /* up */
3085             case 'u':
3086             case 'P':
3087             case 'p':
3088                 if (curr_item > 0)
3089                     --curr_item;
3090                 break;
3091
3092             case 'D':                           /* down */
3093             case 'd':
3094             case 'N':
3095             case 'n':
3096                 if (curr_item < 2)
3097                     ++curr_item;
3098                 break;
3099
3100             case '\r':                          
3101             case '\n':
3102                 clear();
3103                 if (!curr_item)
3104                     return 1;
3105                 else if (curr_item == 1)
3106                     return visuserconfig();
3107                 else {
3108                     putxy(0, 1, "Type \"help\" for help or \"quit\" to exit.");
3109                     /* enable quitfunc */
3110                     userconfig_boot_parsing=0;
3111                     move (0, 3);
3112                     boothowto |= RB_CONFIG;     /* force -c */
3113                     return 0;
3114                 }
3115                 break;
3116             }
3117         }
3118     }
3119 #endif
3120 }
3121
3122 #if NPNP > 0
3123 static int
3124 lspnp ()
3125 {
3126     struct pnp_cinfo *c;
3127     int i, first = 1;
3128
3129
3130     for (i=0; i< MAX_PNP_LDN; i++) {
3131         c = &pnp_ldn_overrides[i];
3132         if (c->csn >0 && c->csn != 255) {
3133             int pmax, mmax;
3134             static char pfmt[] =
3135                 "port 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x ";
3136             static char mfmt[] =
3137                 "mem 0x%x 0x%x 0x%x 0x%x";
3138             char buf[256];
3139             if (lineno >= 23) {
3140                     if (!userconfig_boot_parsing) {
3141                             printf("<More> ");
3142                             if (getchar() == 'q') {
3143                                     printf("quit\n");
3144                                     return (1);
3145                             }
3146                             printf("\n");
3147                     }
3148                     lineno = 0;
3149             }
3150             if (lineno == 0 || first)
3151                 printf("CSN LDN conf en irqs  drqs others (PnP devices)\n");
3152             first = 0 ;
3153             printf("%3d %3d %4s %2s %2d %-2d %2d %-2d ",
3154                 c->csn, c->ldn,
3155                 c->override ? "OS  ":"BIOS",
3156                 c->enable ? "Y":"N",
3157                 c->irq[0], c->irq[1], c->drq[0], c->drq[1]);
3158             if (c->flags)
3159                 printf("flags 0x%08lx ",c->flags);
3160             for (pmax = 7; pmax >=0 ; pmax--)
3161                 if (c->port[pmax]!=0) break;
3162             for (mmax = 3; mmax >=0 ; mmax--)
3163                 if (c->mem[mmax].base!=0) break;
3164             if (pmax>=0) {
3165                 strcpy(buf, pfmt);
3166                 buf[10 + 5*pmax]='\0';
3167                 printf(buf,
3168                     c->port[0], c->port[1], c->port[2], c->port[3],
3169                     c->port[4], c->port[5], c->port[6], c->port[7]);
3170             }
3171             if (mmax>=0) {
3172                 strcpy(buf, mfmt);
3173                 buf[8 + 5*mmax]='\0';
3174                 printf(buf,
3175                     c->mem[0].base, c->mem[1].base,
3176                     c->mem[2].base, c->mem[3].base);
3177             }
3178             printf("\n");
3179         }
3180     }
3181     return 0 ;
3182 }
3183 #endif /* NPNP */
3184                 
3185 static int
3186 lsdevtab(struct uc_device *dt)
3187 {
3188     for (; dt->id_id != 0; dt++) {
3189         char dname[80];
3190
3191         if (lineno >= 23) {
3192                 printf("<More> ");
3193                 if (!userconfig_boot_parsing) {
3194                         if (getchar() == 'q') {
3195                                 printf("quit\n");
3196                                 return (1);
3197                         }
3198                         printf("\n");
3199                 }
3200                 lineno = 0;
3201         }
3202         if (lineno == 0) {
3203                 printf(
3204 "Device   port       irq   drq   iomem   iosize   unit  flags      enab\n"
3205                     );
3206                 ++lineno;
3207         }
3208         sprintf(dname, "%s%d", dt->id_name, dt->id_unit);
3209         printf("%-9.9s%-#11x%-6d%-6d%-8p%-9d%-6d%-#11x%-5s\n",
3210             dname, /* dt->id_id, dt->id_driver(by name), */ dt->id_iobase,
3211             ffs(dt->id_irq) - 1, dt->id_drq, dt->id_maddr, dt->id_msize,
3212             /* dt->id_intr(by name), */ dt->id_unit, dt->id_flags,
3213             dt->id_enabled ? "Yes" : "No");
3214         ++lineno;
3215     }
3216     return(0);
3217 }
3218
3219 static void
3220 load_devtab(void)
3221 {
3222     int i, val;
3223     int count = resource_count();
3224     int id = 1;
3225     int dt;
3226     char *name;
3227     int unit;
3228
3229     uc_devtab = malloc(sizeof(struct uc_device)*(count + 1),M_DEVL,M_WAITOK);
3230     bzero(uc_devtab, sizeof(struct uc_device) * (count + 1));
3231     dt = 0;
3232     for (i = 0; i < count; i++) {
3233         name = resource_query_name(i);
3234         unit = resource_query_unit(i);
3235         if (unit < 0)
3236             continue;   /* skip wildcards */
3237         uc_devtab[dt].id_id = id++;
3238         resource_int_value(name, unit, "port", &uc_devtab[dt].id_iobase);
3239         val = 0;
3240         resource_int_value(name, unit, "irq", &val);
3241         uc_devtab[dt].id_irq = (1 << val);
3242         resource_int_value(name, unit, "drq", &uc_devtab[dt].id_drq);
3243         resource_int_value(name, unit, "maddr",(int *)&uc_devtab[dt].id_maddr);
3244         resource_int_value(name, unit, "msize", &uc_devtab[dt].id_msize);
3245         uc_devtab[dt].id_unit = unit;
3246         resource_int_value(name, unit, "flags", &uc_devtab[dt].id_flags);
3247         val = 0;
3248         resource_int_value(name, unit, "disabled", &val);
3249         uc_devtab[dt].id_enabled = !val;
3250         uc_devtab[dt].id_name = malloc(strlen(name) + 1, M_DEVL,M_WAITOK);
3251         strcpy(uc_devtab[dt].id_name, name);
3252         dt++;
3253     }
3254 }
3255
3256 static void
3257 free_devtab(void)
3258 {
3259     int i;
3260     int count = resource_count();
3261
3262     for (i = 0; i < count; i++)
3263         if (uc_devtab[i].id_name)
3264             free(uc_devtab[i].id_name, M_DEVL);
3265     free(uc_devtab, M_DEVL);
3266 }
3267     
3268 static struct uc_device *
3269 find_device(char *devname, int unit)
3270 {
3271     struct uc_device *ret;
3272
3273     if ((ret = search_devtable(uc_devtab, devname, unit)) != NULL)
3274         return ret;
3275     return NULL;
3276 }
3277
3278 static struct uc_device *
3279 search_devtable(struct uc_device *dt, char *devname, int unit)
3280 {
3281     int i;
3282
3283     for (i = 0; dt->id_id != 0; dt++)
3284         if (!strcmp(dt->id_name, devname) && dt->id_unit == unit)
3285             return dt;
3286     return NULL;
3287 }
3288
3289 static void
3290 cngets(char *input, int maxin)
3291 {
3292     int c, nchars = 0;
3293
3294     while (1) {
3295         c = getchar();
3296         /* Treat ^H or ^? as backspace */
3297         if ((c == '\010' || c == '\177')) {
3298                 if (nchars) {
3299                         printf("\010 \010");
3300                         *--input = '\0', --nchars;
3301                 }
3302                 continue;
3303         }
3304         /* Treat ^U or ^X as kill line */
3305         else if ((c == '\025' || c == '\030')) {
3306                 while (nchars) {
3307                         printf("\010 \010");
3308                         *--input = '\0', --nchars;
3309                 }
3310                 continue;
3311         }
3312         printf("%c", c);
3313         if ((++nchars == maxin) || (c == '\n') || (c == '\r') || ( c == -1)) {
3314             *input = '\0';
3315             break;
3316         }
3317         *input++ = (u_char)c;
3318     }
3319 }
3320
3321
3322 #if 0
3323 /* scsi: Support for displaying configured SCSI devices.
3324  * There is no way to edit them, and this is inconsistent
3325  * with the ISA method.  This is here as a basis for further work.
3326  */
3327 static char *
3328 type_text(char *name)   /* XXX: This is bogus */
3329 {
3330         if (strcmp(name, "sd") == 0)
3331                 return "disk";
3332
3333         if (strcmp(name, "st") == 0)
3334                 return "tape";
3335
3336         return "device";
3337 }
3338
3339 id_put(char *desc, int id)
3340 {
3341     if (id != SCCONF_UNSPEC)
3342     {
3343         if (desc)
3344             printf("%s", desc);
3345
3346         if (id == SCCONF_ANY)
3347             printf("?");
3348         else
3349             printf("%d", id);
3350     }
3351 }
3352
3353 static void
3354 lsscsi(void)
3355 {
3356     int i;
3357
3358     printf("scsi: (can't be edited):\n");
3359
3360     for (i = 0; scsi_cinit[i].driver; i++)
3361     {
3362         id_put("controller scbus", scsi_cinit[i].scbus);
3363
3364         if (scsi_cinit[i].unit != -1)
3365         {
3366             printf(" at ");
3367             id_put(scsi_cinit[i].driver, scsi_cinit[i].unit);
3368         }
3369
3370         printf("\n");
3371     }
3372
3373     for (i = 0; scsi_dinit[i].name; i++)
3374     {
3375                 printf("%s ", type_text(scsi_dinit[i].name));
3376
3377                 id_put(scsi_dinit[i].name, scsi_dinit[i].unit);
3378                 id_put(" at scbus", scsi_dinit[i].cunit);
3379                 id_put(" target ", scsi_dinit[i].target);
3380                 id_put(" lun ", scsi_dinit[i].lun);
3381
3382                 if (scsi_dinit[i].flags)
3383                 printf(" flags 0x%x\n", scsi_dinit[i].flags);
3384
3385                 printf("\n");
3386     }
3387 }
3388
3389 static int
3390 list_scsi(CmdParm *parms)
3391 {
3392     lineno = 0;
3393     lsscsi();
3394     return 0;
3395 }
3396 #endif
3397
3398 static void
3399 save_resource(struct uc_device *idev)
3400 {
3401     char *name;
3402     int unit;
3403
3404     name = idev->id_name;
3405     unit = idev->id_unit;
3406     resource_set_int(name, unit, "port", idev->id_iobase);
3407     resource_set_int(name, unit, "irq", ffs(idev->id_irq) - 1);
3408     resource_set_int(name, unit, "drq", idev->id_drq);
3409     resource_set_int(name, unit, "maddr", (int)idev->id_maddr);
3410     resource_set_int(name, unit, "msize", idev->id_msize);
3411     resource_set_int(name, unit, "flags", idev->id_flags);
3412     resource_set_int(name, unit, "disabled", !idev->id_enabled);
3413 }
3414
3415 static int
3416 save_dev(idev)
3417 struct uc_device        *idev;
3418 {
3419         struct uc_device        *id_p,*id_pn;
3420         char *name = idev->id_name;
3421
3422         for (id_p = uc_devlist; id_p; id_p = id_p->id_next) {
3423                 if (id_p->id_id == idev->id_id) {
3424                         id_pn = id_p->id_next;
3425                         if (id_p->id_name)
3426                                 free(id_p->id_name, M_DEVL);
3427                         bcopy(idev,id_p,sizeof(struct uc_device));
3428                         save_resource(idev);
3429                         id_p->id_name = malloc(strlen(name)+1, M_DEVL,M_WAITOK);
3430                         strcpy(id_p->id_name, name);
3431                         id_p->id_next = id_pn;
3432                         return 1;
3433                 }
3434         }
3435         id_pn = malloc(sizeof(struct uc_device),M_DEVL,M_WAITOK);
3436         bcopy(idev,id_pn,sizeof(struct uc_device));
3437         save_resource(idev);
3438         id_pn->id_name = malloc(strlen(name) + 1, M_DEVL,M_WAITOK);
3439         strcpy(id_pn->id_name, name);
3440         id_pn->id_next = uc_devlist;
3441         uc_devlist = id_pn;
3442         return 0;
3443 }
3444
3445