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