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