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