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