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