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