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