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