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