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