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