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