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