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