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