mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-23 11:18:54 +00:00
3179 lines
81 KiB
C
3179 lines
81 KiB
C
/**
|
|
** Copyright (c) 1995
|
|
** Michael Smith, msmith@freebsd.org. All rights reserved.
|
|
**
|
|
** This code contains a module marked :
|
|
|
|
* Copyright (c) 1991 Regents of the University of California.
|
|
* All rights reserved.
|
|
* Copyright (c) 1994 Jordan K. Hubbard
|
|
* All rights reserved.
|
|
* Copyright (c) 1994 David Greenman
|
|
* All rights reserved.
|
|
*
|
|
* Many additional changes by Bruce Evans
|
|
*
|
|
* This code is derived from software contributed by the
|
|
* University of California Berkeley, Jordan K. Hubbard,
|
|
* David Greenman and Bruce Evans.
|
|
|
|
** As such, it contains code subject to the above copyrights.
|
|
** The module and its copyright can be found below.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer as
|
|
** the first lines of this file unmodified.
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
** documentation and/or other materials provided with the distribution.
|
|
** 3. All advertising materials mentioning features or use of this software
|
|
** must display the following acknowledgment:
|
|
** This product includes software developed by Michael Smith.
|
|
** 4. The name of the author may not be used to endorse or promote products
|
|
** derived from this software without specific prior written permission.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY MICHAEL SMITH ``AS IS'' AND ANY EXPRESS OR
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
** IN NO EVENT SHALL MICHAEL SMITH BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
**
|
|
** $FreeBSD$
|
|
**/
|
|
|
|
/**
|
|
** USERCONFIG
|
|
**
|
|
** Kernel boot-time configuration manipulation tool for FreeBSD.
|
|
**
|
|
** Two modes of operation are supported : the default is the line-editor mode,
|
|
** the command "visual" invokes the fullscreen mode.
|
|
**
|
|
** The line-editor mode is the old favorite from FreeBSD 2.0/20.05 &c., the
|
|
** fullscreen mode requires syscons or a minimal-ansi serial console.
|
|
**/
|
|
|
|
/**
|
|
** USERCONFIG, visual mode.
|
|
**
|
|
** msmith@freebsd.org
|
|
**
|
|
** Look for "EDIT THIS LIST" to add to the list of known devices
|
|
**
|
|
**
|
|
** There are a number of assumptions made in this code.
|
|
**
|
|
** - That the console supports a minimal set of ANSI escape sequences
|
|
** (See the screen manipulation section for a summary)
|
|
** and has at least 24 rows.
|
|
** - That values less than or equal to zero for any of the device
|
|
** parameters indicate that the driver does not use the parameter.
|
|
** - That flags are _always_ editable.
|
|
**
|
|
** Devices marked as disabled are imported as such.
|
|
**
|
|
** For this tool to be useful, the list of devices below _MUST_ be updated
|
|
** when a new driver is brought into the kernel. It is not possible to
|
|
** extract this information from the drivers in the kernel.
|
|
**
|
|
** XXX - TODO:
|
|
**
|
|
** - Display _what_ a device conflicts with.
|
|
** - Implement page up/down (as what?)
|
|
** - Wizard mode (no restrictions)
|
|
** - Find out how to put syscons back into low-intensity mode so that the
|
|
** !b escape is useful on the console. (It seems to be that it actually
|
|
** gets low/high intensity backwards. That looks OK.)
|
|
**
|
|
** - Only display headings with devices under them. (difficult)
|
|
**/
|
|
|
|
/*
|
|
* PC-9801 port by KATO Takenori <kato@eclogite.eps.nagoya-u.ac.jp>
|
|
*/
|
|
|
|
#include "opt_userconfig.h"
|
|
#define COMPAT_OLDISA /* get the definitions */
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/reboot.h>
|
|
#include <sys/linker.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/cons.h>
|
|
|
|
#include <machine/md_var.h>
|
|
#include <machine/limits.h>
|
|
|
|
#define _I386_ISA_ISA_DEVICE_H_
|
|
|
|
static MALLOC_DEFINE(M_DEVL, "uc_devlist", "uc_device lists in userconfig()");
|
|
|
|
#include <machine/uc_device.h>
|
|
static struct uc_device *uc_devlist; /* list read by kget to extract changes */
|
|
static struct uc_device *uc_devtab; /* fake uc_device table */
|
|
|
|
static int userconfig_boot_parsing; /* set if we are reading from the boot instructions */
|
|
|
|
#define putchar(x) cnputc(x)
|
|
|
|
static void load_devtab(void);
|
|
static void free_devtab(void);
|
|
static void save_resource(struct uc_device *);
|
|
|
|
static int
|
|
sysctl_machdep_uc_devlist(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
struct uc_device *id;
|
|
int error=0;
|
|
char name[8];
|
|
|
|
if(!req->oldptr) {
|
|
/* Only sizing */
|
|
id=uc_devlist;
|
|
while(id) {
|
|
error+=sizeof(struct uc_device)+8;
|
|
id=id->id_next;
|
|
}
|
|
return(SYSCTL_OUT(req,0,error));
|
|
} else {
|
|
/* Output the data. The buffer is filled with consecutive
|
|
* struct uc_device and char buf[8], containing the name
|
|
* (not guaranteed to end with '\0').
|
|
*/
|
|
id=uc_devlist;
|
|
while(id) {
|
|
error=sysctl_handle_opaque(oidp,id,
|
|
sizeof(struct uc_device),req);
|
|
if(error) return(error);
|
|
strncpy(name,id->id_name,8);
|
|
error=sysctl_handle_opaque(oidp,name,
|
|
8,req);
|
|
if(error) return(error);
|
|
id=id->id_next;
|
|
}
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
SYSCTL_PROC( _machdep, OID_AUTO, uc_devlist, CTLFLAG_RD,
|
|
0, 0, sysctl_machdep_uc_devlist, "A",
|
|
"List of ISA devices changed in UserConfig");
|
|
|
|
/*
|
|
** Obtain command input.
|
|
**
|
|
** Initially, input is read from a possibly-loaded script.
|
|
** At the end of the script, or if no script is supplied,
|
|
** behaviour is determined by the RB_CONFIG (-c) flag. If
|
|
** the flag is set, user input is read from the console; if
|
|
** unset, the 'quit' command is invoked and userconfig
|
|
** will exit.
|
|
**
|
|
** Note that quit commands encountered in the script will be
|
|
** ignored if the RB_CONFIG flag is supplied.
|
|
*/
|
|
static const char *config_script;
|
|
static int config_script_size; /* use of int for -ve magic value */
|
|
|
|
#define has_config_script() (config_script_size > 0)
|
|
|
|
static int
|
|
init_config_script(void)
|
|
{
|
|
caddr_t autoentry, autoattr;
|
|
|
|
/* Look for loaded userconfig script */
|
|
autoentry = preload_search_by_type("userconfig_script");
|
|
if (autoentry != NULL) {
|
|
/* We have one, get size and data */
|
|
config_script_size = 0;
|
|
if ((autoattr = preload_search_info(autoentry, MODINFO_SIZE)) != NULL)
|
|
config_script_size = (size_t)*(u_int32_t *)autoattr;
|
|
config_script = NULL;
|
|
if ((autoattr = preload_search_info(autoentry, MODINFO_ADDR)) != NULL)
|
|
config_script = *(const char **)autoattr;
|
|
/* sanity check */
|
|
if ((config_script_size == 0) || (config_script == NULL)) {
|
|
config_script_size = 0;
|
|
config_script = NULL;
|
|
}
|
|
}
|
|
return has_config_script();
|
|
}
|
|
|
|
static int
|
|
getchar(void)
|
|
{
|
|
int c = -1;
|
|
#ifdef INTRO_USERCONFIG
|
|
static int intro = 0;
|
|
#endif
|
|
|
|
if (has_config_script())
|
|
{
|
|
/* Consume character from loaded userconfig script, display */
|
|
userconfig_boot_parsing = 1;
|
|
c = *config_script;
|
|
config_script++;
|
|
config_script_size--;
|
|
|
|
} else {
|
|
|
|
#ifdef INTRO_USERCONFIG
|
|
if (userconfig_boot_parsing) {
|
|
if (!(boothowto & RB_CONFIG)) {
|
|
/* userconfig_script, !RB_CONFIG -> quit */
|
|
if (intro == 0) {
|
|
c = 'q';
|
|
config_script = "uit\n";
|
|
config_script_size = strlen(config_script);
|
|
/* userconfig_script will be 1 on the next pass */
|
|
}
|
|
} else {
|
|
/* userconfig_script, RB_CONFIG -> cngetc() */
|
|
}
|
|
} else {
|
|
if (!(boothowto & RB_CONFIG)) {
|
|
/* no userconfig_script, !RB_CONFIG -> show intro */
|
|
if (intro == 0) {
|
|
intro = 1;
|
|
c = 'i';
|
|
config_script = "ntro\n";
|
|
config_script_size = strlen(config_script);
|
|
/* userconfig_script will be 1 on the next pass */
|
|
}
|
|
} else {
|
|
/* no userconfig_script, RB_CONFIG -> cngetc() */
|
|
}
|
|
}
|
|
#else /* !INTRO_USERCONFIG */
|
|
/* assert(boothowto & RB_CONFIG) */
|
|
#endif /* INTRO_USERCONFIG */
|
|
userconfig_boot_parsing = 0;
|
|
if (c <= 0)
|
|
c = cngetc();
|
|
}
|
|
return(c);
|
|
}
|
|
|
|
#ifndef FALSE
|
|
#define FALSE (0)
|
|
#define TRUE (!FALSE)
|
|
#endif
|
|
|
|
#ifdef VISUAL_USERCONFIG
|
|
|
|
typedef struct
|
|
{
|
|
char dev[16]; /* device basename */
|
|
char name[60]; /* long name */
|
|
int attrib; /* things to do with the device */
|
|
int class; /* device classification */
|
|
} DEV_INFO;
|
|
|
|
#define FLG_INVISIBLE (1<<0) /* device should not be shown */
|
|
#define FLG_MANDATORY (1<<1) /* device can be edited but not disabled */
|
|
#define FLG_FIXIRQ (1<<2) /* device IRQ cannot be changed */
|
|
#define FLG_FIXIOBASE (1<<3) /* device iobase cannot be changed */
|
|
#define FLG_FIXMADDR (1<<4) /* device maddr cannot be changed */
|
|
#define FLG_FIXMSIZE (1<<5) /* device msize cannot be changed */
|
|
#define FLG_FIXDRQ (1<<6) /* device DRQ cannot be changed */
|
|
#define FLG_FIXED (FLG_FIXIRQ|FLG_FIXIOBASE|FLG_FIXMADDR|FLG_FIXMSIZE|FLG_FIXDRQ)
|
|
#define FLG_IMMUTABLE (FLG_FIXED|FLG_MANDATORY)
|
|
|
|
#define CLS_STORAGE 1 /* storage devices */
|
|
#define CLS_NETWORK 2 /* network interfaces */
|
|
#define CLS_COMMS 3 /* serial, parallel ports */
|
|
#define CLS_INPUT 4 /* user input : mice, keyboards, joysticks etc */
|
|
#define CLS_MMEDIA 5 /* "multimedia" devices (sound, video, etc) */
|
|
#define CLS_MISC 255 /* none of the above */
|
|
|
|
|
|
typedef struct
|
|
{
|
|
char name[60];
|
|
int number;
|
|
} DEVCLASS_INFO;
|
|
|
|
static DEVCLASS_INFO devclass_names[] = {
|
|
{ "Storage : ", CLS_STORAGE},
|
|
{ "Network : ", CLS_NETWORK},
|
|
{ "Communications : ", CLS_COMMS},
|
|
{ "Input : ", CLS_INPUT},
|
|
{ "Multimedia : ", CLS_MMEDIA},
|
|
{ "Miscellaneous : ", CLS_MISC},
|
|
{ "",0}};
|
|
|
|
|
|
/********************* EDIT THIS LIST **********************/
|
|
|
|
/** Notes :
|
|
**
|
|
** - Devices that shouldn't be seen or removed should be marked FLG_INVISIBLE.
|
|
** - XXX The list below should be reviewed by the driver authors to verify
|
|
** that the correct flags have been set for each driver, and that the
|
|
** descriptions are accurate.
|
|
**/
|
|
|
|
static DEV_INFO device_info[] = {
|
|
/*---Name----- ---Description---------------------------------------------- */
|
|
#ifdef PC98
|
|
{"bs", "PC-9801-55 SCSI Interface", 0, CLS_STORAGE},
|
|
{"wdc", "IDE/ESDI/MFM disk controller", 0, CLS_STORAGE},
|
|
#endif
|
|
{"adv", "AdvanSys SCSI narrow controller", 0, CLS_STORAGE},
|
|
{"bt", "Buslogic SCSI controller", 0, CLS_STORAGE},
|
|
{"aha", "Adaptec 154x SCSI controller", 0, CLS_STORAGE},
|
|
{"aic", "Adaptec 152x SCSI and compatible SCSI cards", 0, CLS_STORAGE},
|
|
{"nca", "ProAudio Spectrum SCSI and compatibles", 0, CLS_STORAGE},
|
|
{"sea", "Seagate ST01/ST02 SCSI and compatibles", 0, CLS_STORAGE},
|
|
{"stg", "TMC 18C30/18C50 based SCSI cards", 0, CLS_STORAGE},
|
|
{"ata", "ATA/ATAPI compatible disk controller", 0, CLS_STORAGE},
|
|
{"fdc", "Floppy disk controller", FLG_FIXED, CLS_STORAGE},
|
|
{"mcd", "Mitsumi CD-ROM", 0, CLS_STORAGE},
|
|
{"scd", "Sony CD-ROM", 0, CLS_STORAGE},
|
|
{"matcd", "Matsushita/Panasonic/Creative CDROM", 0, CLS_STORAGE},
|
|
{"wt", "Wangtek/Archive QIC-02 Tape drive", 0, CLS_STORAGE},
|
|
{"wd", "IDE or ST506 compatible storage device", FLG_INVISIBLE, CLS_STORAGE},
|
|
{"ad", "ATA/ATAPI compatible storage device", FLG_INVISIBLE, CLS_STORAGE},
|
|
{"fd", "Floppy disk device", FLG_INVISIBLE, CLS_STORAGE},
|
|
|
|
{"cs", "IBM EtherJet, CS89x0-based Ethernet adapters",0, CLS_NETWORK},
|
|
#ifdef PC98
|
|
{"ed", "NS8390 Ethernet adapters", 0, CLS_NETWORK},
|
|
#else
|
|
{"ed", "NE1000,NE2000,3C503,WD/SMC80xx Ethernet adapters",0, CLS_NETWORK},
|
|
#endif
|
|
{"el", "3C501 Ethernet adapter", 0, CLS_NETWORK},
|
|
{"ep", "3C509 Ethernet adapter", 0, CLS_NETWORK},
|
|
{"ex", "Intel EtherExpress Pro/10 Ethernet adapter", 0, CLS_NETWORK},
|
|
{"fe", "Fujitsu MB86960A/MB86965A Ethernet adapters", 0, CLS_NETWORK},
|
|
{"ie", "AT&T Starlan 10 and EN100, 3C507, NI5210 Ethernet adapters",0,CLS_NETWORK},
|
|
{"le", "DEC Etherworks 2 and 3 Ethernet adapters", 0, CLS_NETWORK},
|
|
{"lnc", "Isolan, Novell NE2100/NE32-VL Ethernet adapters", 0,CLS_NETWORK},
|
|
{"sn", "SMC/Megahertz Ethernet adapters", 0,CLS_NETWORK},
|
|
{"snc", "SONIC Ethernet adapters", 0,CLS_NETWORK},
|
|
{"xe", "Xircom PC Card Ethernet adapter", 0, CLS_NETWORK},
|
|
{"rdp", "RealTek RTL8002 Pocket Ethernet", 0, CLS_NETWORK},
|
|
|
|
{"sio", "8250/16450/16550 Serial port", 0, CLS_COMMS},
|
|
{"cx", "Cronyx/Sigma multiport sync/async adapter",0, CLS_COMMS},
|
|
{"rc", "RISCom/8 multiport async adapter", 0, CLS_COMMS},
|
|
{"cy", "Cyclades multiport async adapter", 0, CLS_COMMS},
|
|
{"dgb", "Digiboard PC/Xe, PC/Xi async adapter", 0, CLS_COMMS},
|
|
{"si", "Specialix SI/XIO/SX async adapter", 0, CLS_COMMS},
|
|
{"stl", "Stallion EasyIO/Easy Connection 8/32 async adapter",0, CLS_COMMS},
|
|
{"stli", "Stallion intelligent async adapter" ,0, CLS_COMMS},
|
|
#ifdef PC98
|
|
{"olpt", "Parallel printer port", 0, CLS_COMMS},
|
|
#endif
|
|
{"ppc", "Parallel Port chipset", 0, CLS_COMMS},
|
|
{"gp", "National Instruments AT-GPIB/TNT driver", 0, CLS_COMMS},
|
|
|
|
{"atkbdc", "Keyboard controller", FLG_INVISIBLE, CLS_INPUT},
|
|
{"atkbd", "Keyboard", FLG_FIXED, CLS_INPUT},
|
|
#ifdef PC98
|
|
{"pckbd", "Keyboard", FLG_FIXED, CLS_INPUT},
|
|
#endif
|
|
{"mse", "Microsoft Bus Mouse", 0, CLS_INPUT},
|
|
{"psm", "PS/2 Mouse", FLG_FIXED, CLS_INPUT},
|
|
{"joy", "Joystick", FLG_FIXED, CLS_INPUT},
|
|
{"vt", "PCVT console driver", FLG_IMMUTABLE, CLS_INPUT},
|
|
{"sc", "Syscons console driver", FLG_IMMUTABLE, CLS_INPUT},
|
|
|
|
#ifdef PC98
|
|
{"nss", "PC-9801-86 Sound Board", 0, CLS_MMEDIA},
|
|
#endif
|
|
{"sbc", "PCM Creative SoundBlaster/ESS/Avance sounce cards", 0,CLS_MMEDIA},
|
|
{"gusc", "PCM Gravis UltraSound sound cards", 0, CLS_MMEDIA},
|
|
{"pcm", "PCM Generic soundcard support", 0, CLS_MMEDIA},
|
|
{"sb", "VOXWARE Soundblaster PCM (SB/Pro/16, ProAudio Spectrum)",0,CLS_MMEDIA},
|
|
{"sbxvi", "VOXWARE Soundblaster 16", 0, CLS_MMEDIA},
|
|
{"sbmidi", "VOXWARE Soundblaster MIDI interface", 0, CLS_MMEDIA},
|
|
{"awe", "VOXWARE AWE32 MIDI", 0, CLS_MMEDIA},
|
|
{"pas", "VOXWARE ProAudio Spectrum PCM and MIDI", 0, CLS_MMEDIA},
|
|
{"gus", "VOXWARE Gravis Ultrasound, Ultrasound 16 and Ultrasound MAX",0,CLS_MMEDIA},
|
|
{"gusxvi", "VOXWARE Gravis Ultrasound 16-bit PCM", 0, CLS_MMEDIA},
|
|
{"gusmax", "VOXWARE Gravis Ultrasound MAX", 0, CLS_MMEDIA},
|
|
{"mss", "VOXWARE Microsoft Sound System", 0, CLS_MMEDIA},
|
|
{"opl", "VOXWARE OPL-2/3 FM, SB/Pro/16, ProAudio Spectrum",0,CLS_MMEDIA},
|
|
{"mpu", "VOXWARE Roland MPU401 MIDI", 0, CLS_MMEDIA},
|
|
{"sscape", "VOXWARE Ensoniq Soundscape MIDI interface", 0, CLS_MMEDIA},
|
|
{"sscape_mss", "VOXWARE Ensoniq Soundscape PCM", 0, CLS_MMEDIA},
|
|
{"uart", "VOXWARE 6850 MIDI UART", 0, CLS_MMEDIA},
|
|
{"pca", "PC speaker PCM audio driver", FLG_FIXED, CLS_MMEDIA},
|
|
{"ctx", "Coretex-I frame grabber", 0, CLS_MMEDIA},
|
|
{"spigot", "Creative Labs Video Spigot video capture", 0, CLS_MMEDIA},
|
|
{"scc", "IBM Smart Capture Card", 0, CLS_MMEDIA},
|
|
{"gsc", "Genius GS-4500 hand scanner", 0, CLS_MMEDIA},
|
|
{"asc", "AmiScan scanner", 0, CLS_MMEDIA},
|
|
|
|
{"apm", "Advanced Power Management", FLG_FIXED, CLS_MISC},
|
|
{"pcic", "PC-card controller", 0, CLS_MISC},
|
|
{"npx", "Math coprocessor", FLG_IMMUTABLE, CLS_MISC},
|
|
#ifdef PC98
|
|
{"gdc", "Graphic Display Controller", FLG_INVISIBLE, CLS_MISC},
|
|
#endif
|
|
{"vga", "Catchall PCI VGA driver", FLG_INVISIBLE, CLS_MISC},
|
|
{"","",0,0}};
|
|
|
|
|
|
typedef struct _devlist_struct
|
|
{
|
|
char name[80];
|
|
int attrib; /* flag values as per the FLG_* defines above */
|
|
int class; /* disk, etc as per the CLS_* defines above */
|
|
char dev[16];
|
|
int iobase,irq,drq,maddr,msize,unit,flags,id;
|
|
int comment; /* 0 = device, 1 = comment, 2 = collapsed comment */
|
|
int conflicts; /* set/reset by findconflict, count of conflicts */
|
|
int changed; /* nonzero if the device has been edited */
|
|
struct uc_device *device;
|
|
struct _devlist_struct *prev,*next;
|
|
} DEV_LIST;
|
|
|
|
|
|
#define DEV_DEVICE 0
|
|
#define DEV_COMMENT 1
|
|
#define DEV_ZOOMED 2
|
|
|
|
#define LIST_CURRENT (1<<0)
|
|
#define LIST_SELECTED (1<<1)
|
|
|
|
#define KEY_EXIT 0 /* return codes from dolist() and friends */
|
|
#define KEY_DO 1
|
|
#define KEY_DEL 2
|
|
#define KEY_TAB 3
|
|
#define KEY_REDRAW 4
|
|
|
|
#define KEY_UP 5 /* these only returned from editval() */
|
|
#define KEY_DOWN 6
|
|
#define KEY_LEFT 7
|
|
#define KEY_RIGHT 8
|
|
#define KEY_NULL 9 /* this allows us to spin & redraw */
|
|
|
|
#define KEY_ZOOM 10 /* these for zoom all/collapse all */
|
|
#define KEY_UNZOOM 11
|
|
|
|
#define KEY_HELP 12 /* duh? */
|
|
|
|
static void redraw(void);
|
|
static void insdev(DEV_LIST *dev, DEV_LIST *list);
|
|
static int devinfo(DEV_LIST *dev);
|
|
static int visuserconfig(void);
|
|
|
|
static DEV_LIST *active = NULL,*inactive = NULL; /* driver lists */
|
|
static DEV_LIST *alist,*ilist; /* visible heads of the driver lists */
|
|
static DEV_LIST scratch; /* scratch record */
|
|
static int conflicts; /* total conflict count */
|
|
|
|
|
|
static char lines[] = "--------------------------------------------------------------------------------";
|
|
static char spaces[] = " ";
|
|
|
|
|
|
/**
|
|
** Device manipulation stuff : find, describe, configure.
|
|
**/
|
|
|
|
/**
|
|
** setdev
|
|
**
|
|
** Sets the device referenced by (*dev) to the parameters in the struct,
|
|
** and the enable flag according to (enabled)
|
|
**/
|
|
static void
|
|
setdev(DEV_LIST *dev, int enabled)
|
|
{
|
|
dev->device->id_iobase = dev->iobase; /* copy happy */
|
|
dev->device->id_irq = (u_short)(dev->irq < 16 ? 1<<dev->irq : 0); /* IRQ is bitfield */
|
|
dev->device->id_drq = (short)dev->drq;
|
|
dev->device->id_maddr = (caddr_t)dev->maddr;
|
|
dev->device->id_msize = dev->msize;
|
|
dev->device->id_flags = dev->flags;
|
|
dev->device->id_enabled = enabled;
|
|
}
|
|
|
|
|
|
/**
|
|
** getdevs
|
|
**
|
|
** Walk the kernel device tables and build the active and inactive lists
|
|
**/
|
|
static void
|
|
getdevs(void)
|
|
{
|
|
int i;
|
|
struct uc_device *ap;
|
|
|
|
ap = uc_devtab; /* pointer to array of devices */
|
|
for (i = 0; ap[i].id_id; i++) /* for each device in this table */
|
|
{
|
|
scratch.unit = ap[i].id_unit; /* device parameters */
|
|
strcpy(scratch.dev,ap[i].id_name);
|
|
scratch.iobase = ap[i].id_iobase;
|
|
scratch.irq = ffs(ap[i].id_irq)-1;
|
|
scratch.drq = ap[i].id_drq;
|
|
scratch.maddr = (int)ap[i].id_maddr;
|
|
scratch.msize = ap[i].id_msize;
|
|
scratch.flags = ap[i].id_flags;
|
|
|
|
scratch.comment = DEV_DEVICE; /* admin stuff */
|
|
scratch.conflicts = 0;
|
|
scratch.device = &ap[i]; /* save pointer for later reference */
|
|
scratch.changed = 0;
|
|
if (!devinfo(&scratch)) /* get more info on the device */
|
|
insdev(&scratch,ap[i].id_enabled?active:inactive);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
** Devinfo
|
|
**
|
|
** Fill in (dev->name), (dev->attrib) and (dev->type) from the device_info array.
|
|
** If the device is unknown, put it in the CLS_MISC class, with no flags.
|
|
**
|
|
** If the device is marked "invisible", return nonzero; the caller should
|
|
** not insert any such device into either list.
|
|
**
|
|
**/
|
|
static int
|
|
devinfo(DEV_LIST *dev)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; device_info[i].class; i++)
|
|
{
|
|
if (!strcmp(dev->dev,device_info[i].dev))
|
|
{
|
|
if (device_info[i].attrib & FLG_INVISIBLE) /* forget we ever saw this one */
|
|
return(1);
|
|
strcpy(dev->name,device_info[i].name); /* get the name */
|
|
dev->attrib = device_info[i].attrib;
|
|
dev->class = device_info[i].class;
|
|
return(0);
|
|
}
|
|
}
|
|
strcpy(dev->name,"Unknown device");
|
|
dev->attrib = 0;
|
|
dev->class = CLS_MISC;
|
|
return(0);
|
|
}
|
|
|
|
|
|
/**
|
|
** List manipulation stuff : add, move, initialise, free, traverse
|
|
**
|
|
** Note that there are assumptions throughout this code that
|
|
** the first entry in a list will never move. (assumed to be
|
|
** a comment).
|
|
**/
|
|
|
|
|
|
/**
|
|
** Adddev
|
|
**
|
|
** appends a copy of (dev) to the end of (*list)
|
|
**/
|
|
static void
|
|
addev(DEV_LIST *dev, DEV_LIST **list)
|
|
{
|
|
|
|
DEV_LIST *lp,*ap;
|
|
|
|
lp = (DEV_LIST *)malloc(sizeof(DEV_LIST),M_DEVL,M_WAITOK);
|
|
bcopy(dev,lp,sizeof(DEV_LIST)); /* create copied record */
|
|
|
|
if (*list) /* list exists */
|
|
{
|
|
ap = *list;
|
|
while(ap->next)
|
|
ap = ap->next; /* scoot to end of list */
|
|
lp->prev = ap;
|
|
lp->next = NULL;
|
|
ap->next = lp;
|
|
}else{ /* list does not yet exist */
|
|
*list = lp;
|
|
lp->prev = lp->next = NULL; /* list now exists */
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
** Findspot
|
|
**
|
|
** Finds the 'appropriate' place for (dev) in (list)
|
|
**
|
|
** 'Appropriate' means in numeric order with other devices of the same type,
|
|
** or in alphabetic order following a comment of the appropriate type.
|
|
** or at the end of the list if an appropriate comment is not found. (this should
|
|
** never happen)
|
|
** (Note that the appropriate point is never the top, but may be the bottom)
|
|
**/
|
|
static DEV_LIST *
|
|
findspot(DEV_LIST *dev, DEV_LIST *list)
|
|
{
|
|
DEV_LIST *ap = NULL;
|
|
|
|
/* search for a previous instance of the same device */
|
|
for (ap = list; ap; ap = ap->next)
|
|
{
|
|
if (ap->comment != DEV_DEVICE) /* ignore comments */
|
|
continue;
|
|
if (!strcmp(dev->dev,ap->dev)) /* same base device */
|
|
{
|
|
if ((dev->unit <= ap->unit) /* belongs before (equal is bad) */
|
|
|| !ap->next) /* or end of list */
|
|
{
|
|
ap = ap->prev; /* back up one */
|
|
break; /* done here */
|
|
}
|
|
if (ap->next) /* if the next item exists */
|
|
{
|
|
if (ap->next->comment != DEV_DEVICE) /* next is a comment */
|
|
break;
|
|
if (strcmp(dev->dev,ap->next->dev)) /* next is a different device */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ap) /* not sure yet */
|
|
{
|
|
/* search for a class that the device might belong to */
|
|
for (ap = list; ap; ap = ap->next)
|
|
{
|
|
if (ap->comment != DEV_DEVICE) /* look for simlar devices */
|
|
continue;
|
|
if (dev->class != ap->class) /* of same class too 8) */
|
|
continue;
|
|
if (strcmp(dev->dev,ap->dev) < 0) /* belongs before the current entry */
|
|
{
|
|
ap = ap->prev; /* back up one */
|
|
break; /* done here */
|
|
}
|
|
if (ap->next) /* if the next item exists */
|
|
if (ap->next->comment != DEV_DEVICE) /* next is a comment, go here */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!ap) /* didn't find a match */
|
|
{
|
|
for (ap = list; ap->next; ap = ap->next) /* try for a matching comment */
|
|
if ((ap->comment != DEV_DEVICE)
|
|
&& (ap->class == dev->class)) /* appropriate place? */
|
|
break;
|
|
} /* or just put up with last */
|
|
|
|
return(ap);
|
|
}
|
|
|
|
|
|
/**
|
|
** Insdev
|
|
**
|
|
** Inserts a copy of (dev) at the appropriate point in (list)
|
|
**/
|
|
static void
|
|
insdev(DEV_LIST *dev, DEV_LIST *list)
|
|
{
|
|
DEV_LIST *lp,*ap;
|
|
|
|
lp = (DEV_LIST *)malloc(sizeof(DEV_LIST),M_DEVL,M_WAITOK);
|
|
bcopy(dev,lp,sizeof(DEV_LIST)); /* create copied record */
|
|
|
|
ap = findspot(lp,list); /* find appropriate spot */
|
|
lp->next = ap->next; /* point to next */
|
|
if (ap->next)
|
|
ap->next->prev = lp; /* point next to new */
|
|
lp->prev = ap; /* point new to current */
|
|
ap->next = lp; /* and current to new */
|
|
}
|
|
|
|
|
|
/**
|
|
** Movedev
|
|
**
|
|
** Moves (dev) from its current list to an appropriate place in (list)
|
|
** (dev) may not come from the top of a list, but it may from the bottom.
|
|
**/
|
|
static void
|
|
movedev(DEV_LIST *dev, DEV_LIST *list)
|
|
{
|
|
DEV_LIST *ap;
|
|
|
|
ap = findspot(dev,list);
|
|
dev->prev->next = dev->next; /* remove from old list */
|
|
if (dev->next)
|
|
dev->next->prev = dev->prev;
|
|
|
|
dev->next = ap->next; /* insert in new list */
|
|
if (ap->next)
|
|
ap->next->prev = dev; /* point next to new */
|
|
dev->prev = ap; /* point new to current */
|
|
ap->next = dev; /* and current to new */
|
|
}
|
|
|
|
|
|
/**
|
|
** Initlist
|
|
**
|
|
** Initialises (*list) with the basic headings
|
|
**/
|
|
static void
|
|
initlist(DEV_LIST **list)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; devclass_names[i].name[0]; i++) /* for each devtype name */
|
|
{
|
|
strcpy(scratch.name,devclass_names[i].name);
|
|
scratch.comment = DEV_ZOOMED;
|
|
scratch.class = devclass_names[i].number;
|
|
scratch.attrib = FLG_MANDATORY; /* can't be moved */
|
|
addev(&scratch,list); /* add to the list */
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
** savelist
|
|
**
|
|
** Walks (list) and saves the settings of any entry marked as changed.
|
|
**
|
|
** The device's active field is set according to (active).
|
|
**
|
|
** Builds the uc_devlist used by kget to extract the changed device information.
|
|
** The code for this was taken almost verbatim from the original module.
|
|
**/
|
|
static void
|
|
savelist(DEV_LIST *list, int active)
|
|
{
|
|
struct uc_device *id_p,*id_pn;
|
|
char *name;
|
|
|
|
while (list)
|
|
{
|
|
if ((list->comment == DEV_DEVICE) && /* is a device */
|
|
(list->changed) && /* has been changed */
|
|
(list->device != NULL)) { /* has an uc_device structure */
|
|
|
|
setdev(list,active); /* set the device itself */
|
|
|
|
id_pn = NULL;
|
|
for (id_p=uc_devlist; id_p; id_p=id_p->id_next)
|
|
{ /* look on the list for it */
|
|
if (id_p->id_id == list->device->id_id)
|
|
{
|
|
name = list->device->id_name;
|
|
id_pn = id_p->id_next;
|
|
if (id_p->id_name)
|
|
free(id_p->id_name, M_DEVL);
|
|
bcopy(list->device,id_p,sizeof(struct uc_device));
|
|
save_resource(list->device);
|
|
id_p->id_name = malloc(strlen(name) + 1, M_DEVL,M_WAITOK);
|
|
strcpy(id_p->id_name, name);
|
|
id_pn->id_next = uc_devlist;
|
|
id_p->id_next = id_pn;
|
|
break;
|
|
}
|
|
}
|
|
if (!id_pn) /* not already on the list */
|
|
{
|
|
name = list->device->id_name;
|
|
id_pn = malloc(sizeof(struct uc_device),M_DEVL,M_WAITOK);
|
|
bcopy(list->device,id_pn,sizeof(struct uc_device));
|
|
save_resource(list->device);
|
|
id_pn->id_name = malloc(strlen(name) + 1, M_DEVL,M_WAITOK);
|
|
strcpy(id_pn->id_name, name);
|
|
id_pn->id_next = uc_devlist;
|
|
uc_devlist = id_pn; /* park at top of list */
|
|
}
|
|
}
|
|
list = list->next;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
** nukelist
|
|
**
|
|
** Frees all storage in use by a (list).
|
|
**/
|
|
static void
|
|
nukelist(DEV_LIST *list)
|
|
{
|
|
DEV_LIST *dp;
|
|
|
|
if (!list)
|
|
return;
|
|
while(list->prev) /* walk to head of list */
|
|
list = list->prev;
|
|
|
|
while(list)
|
|
{
|
|
dp = list;
|
|
list = list->next;
|
|
free(dp,M_DEVL);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
** prevent
|
|
**
|
|
** Returns the previous entry in (list), skipping zoomed regions. Returns NULL
|
|
** if there is no previous entry. (Only possible if list->prev == NULL given the
|
|
** premise that there is always a comment at the head of the list)
|
|
**/
|
|
static DEV_LIST *
|
|
prevent(DEV_LIST *list)
|
|
{
|
|
DEV_LIST *dp;
|
|
|
|
if (!list)
|
|
return(NULL);
|
|
dp = list->prev; /* start back one */
|
|
while(dp)
|
|
{
|
|
if (dp->comment == DEV_ZOOMED) /* previous section is zoomed */
|
|
return(dp); /* so skip to comment */
|
|
if (dp->comment == DEV_COMMENT) /* not zoomed */
|
|
return(list->prev); /* one back as normal */
|
|
dp = dp->prev; /* backpedal */
|
|
}
|
|
return(dp); /* NULL, we can assume */
|
|
}
|
|
|
|
|
|
/**
|
|
** nextent
|
|
**
|
|
** Returns the next entry in (list), skipping zoomed regions. Returns NULL
|
|
** if there is no next entry. (Possible if the current entry is last, or
|
|
** if the current entry is the last heading and it's collapsed)
|
|
**/
|
|
static DEV_LIST *
|
|
nextent(DEV_LIST *list)
|
|
{
|
|
DEV_LIST *dp;
|
|
|
|
if (!list)
|
|
return(NULL);
|
|
if (list->comment != DEV_ZOOMED) /* no reason to skip */
|
|
return(list->next);
|
|
dp = list->next;
|
|
while(dp)
|
|
{
|
|
if (dp->comment != DEV_DEVICE) /* found another heading */
|
|
break;
|
|
dp = dp->next;
|
|
}
|
|
return(dp); /* back we go */
|
|
}
|
|
|
|
|
|
/**
|
|
** ofsent
|
|
**
|
|
** Returns the (ofs)th entry down from (list), or NULL if it doesn't exist
|
|
**/
|
|
static DEV_LIST *
|
|
ofsent(int ofs, DEV_LIST *list)
|
|
{
|
|
while (ofs-- && list)
|
|
list = nextent(list);
|
|
return(list);
|
|
}
|
|
|
|
|
|
/**
|
|
** findconflict
|
|
**
|
|
** Scans every element of (list) and sets the conflict tags appropriately
|
|
** Returns the number of conflicts found.
|
|
**/
|
|
static int
|
|
findconflict(DEV_LIST *list)
|
|
{
|
|
int count = 0; /* number of conflicts found */
|
|
DEV_LIST *dp,*sp;
|
|
|
|
for (dp = list; dp; dp = dp->next) /* over the whole list */
|
|
{
|
|
if (dp->comment != DEV_DEVICE) /* comments don't usually conflict */
|
|
continue;
|
|
|
|
dp->conflicts = 0; /* assume the best */
|
|
for (sp = list; sp; sp = sp->next) /* scan the entire list for conflicts */
|
|
{
|
|
if (sp->comment != DEV_DEVICE) /* likewise */
|
|
continue;
|
|
|
|
if (sp == dp) /* always conflict with itself */
|
|
continue;
|
|
|
|
if ((dp->iobase > 0) && /* iobase conflict? */
|
|
(dp->iobase == sp->iobase))
|
|
dp->conflicts = 1;
|
|
if ((dp->irq > 0) && /* irq conflict? */
|
|
(dp->irq == sp->irq))
|
|
dp->conflicts = 1;
|
|
if ((dp->drq > 0) && /* drq conflict? */
|
|
(dp->drq == sp->drq))
|
|
dp->conflicts = 1;
|
|
if ((sp->maddr > 0) && /* maddr/msize conflict? */
|
|
(dp->maddr > 0) &&
|
|
(sp->maddr + ((sp->msize == 0) ? 1 : sp->msize) > dp->maddr) &&
|
|
(dp->maddr + ((dp->msize == 0) ? 1 : dp->msize) > sp->maddr))
|
|
dp->conflicts = 1;
|
|
}
|
|
count += dp->conflicts; /* count conflicts */
|
|
}
|
|
return(count);
|
|
}
|
|
|
|
|
|
/**
|
|
** expandlist
|
|
**
|
|
** Unzooms all headings in (list)
|
|
**/
|
|
static void
|
|
expandlist(DEV_LIST *list)
|
|
{
|
|
while(list)
|
|
{
|
|
if (list->comment == DEV_COMMENT)
|
|
list->comment = DEV_ZOOMED;
|
|
list = list->next;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
** collapselist
|
|
**
|
|
** Zooms all headings in (list)
|
|
**/
|
|
static void
|
|
collapselist(DEV_LIST *list)
|
|
{
|
|
while(list)
|
|
{
|
|
if (list->comment == DEV_ZOOMED)
|
|
list->comment = DEV_COMMENT;
|
|
list = list->next;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
** Screen-manipulation stuff
|
|
**
|
|
** This is the basic screen layout :
|
|
**
|
|
** 0 5 10 15 20 25 30 35 40 45 50 55 60 67 70 75
|
|
** |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|....
|
|
** +--------------------------------------------------------------------------------+
|
|
** 0 -|---Active Drivers----------------------------xx Conflicts------Dev---IRQ--Port--|
|
|
** 1 -| ........................ ....... .. 0x....|
|
|
** 2 -| ........................ ....... .. 0x....|
|
|
** 3 -| ........................ ....... .. 0x....|
|
|
** 4 -| ........................ ....... .. 0x....|
|
|
** 5 -| ........................ ....... .. 0x....|
|
|
** 6 -| ........................ ....... .. 0x....|
|
|
** 7 -| ........................ ....... .. 0x....|
|
|
** 8 -| ........................ ....... .. 0x....|
|
|
** 9 -|---Inactive Drivers--------------------------------------------Dev--------------|
|
|
** 10-| ........................ ....... |
|
|
** 11-| ........................ ....... |
|
|
** 12-| ........................ ....... |
|
|
** 13-| ........................ ....... |
|
|
** 14-| ........................ ....... |
|
|
** 15-| ........................ ....... |
|
|
** 16-| ........................ ....... |
|
|
** 17-|------------------------------------------------------UP-DOWN-------------------|
|
|
** 18-| Relevant parameters for the current device |
|
|
** 19-| |
|
|
** 20-| |
|
|
** 21-|--------------------------------------------------------------------------------|
|
|
** 22-| Help texts go here |
|
|
** 23-| |
|
|
** +--------------------------------------------------------------------------------+
|
|
**
|
|
** Help texts
|
|
**
|
|
** On a collapsed comment :
|
|
**
|
|
** [Enter] Expand device list [z] Expand all lists
|
|
** [TAB] Change fields [Q] Save and Exit
|
|
**
|
|
** On an expanded comment :
|
|
**
|
|
** [Enter] Collapse device list [Z] Collapse all lists
|
|
** [TAB] Change fields [Q] Save and Exit
|
|
**
|
|
** On a comment with no followers
|
|
**
|
|
**
|
|
** [TAB] Change fields [Q] Save and Exit
|
|
**
|
|
** On a device in the active list
|
|
**
|
|
** [Enter] Edit device parameters [DEL] Disable device
|
|
** [TAB] Change fields [Q] Save and Exit [?] Help
|
|
**
|
|
** On a device in the inactive list
|
|
**
|
|
** [Enter] Enable device
|
|
** [TAB] Change fields [Q] Save and Exit [?] Help
|
|
**
|
|
** While editing parameters
|
|
**
|
|
** <parameter-specific help here>
|
|
** [TAB] Change fields [Q] Save device parameters
|
|
**/
|
|
|
|
|
|
|
|
/**
|
|
**
|
|
** The base-level screen primitives :
|
|
**
|
|
** bold() - enter bold mode \E[1m (md)
|
|
** inverse() - enter inverse mode \E[7m (so)
|
|
** normal() - clear bold/inverse mode \E[m (se)
|
|
** clear() - clear the screen \E[H\E[J (ce)
|
|
** move(x,y) - move the cursor to x,y \E[y;xH: (cm)
|
|
**/
|
|
|
|
static void
|
|
bold(void)
|
|
{
|
|
printf("\033[1m");
|
|
}
|
|
|
|
static void
|
|
inverse(void)
|
|
{
|
|
printf("\033[7m");
|
|
}
|
|
|
|
static void
|
|
normal(void)
|
|
{
|
|
printf("\033[m");
|
|
}
|
|
|
|
static void
|
|
clear(void)
|
|
{
|
|
normal();
|
|
printf("\033[H\033[J");
|
|
}
|
|
|
|
static void
|
|
move(int x, int y)
|
|
{
|
|
printf("\033[%d;%dH",y+1,x+1);
|
|
}
|
|
|
|
|
|
/**
|
|
**
|
|
** High-level screen primitives :
|
|
**
|
|
** putxyl(x,y,str,len) - put (len) bytes of (str) at (x,y), supports embedded formatting
|
|
** putxy(x,y,str) - put (str) at (x,y), supports embedded formatting
|
|
** erase(x,y,w,h) - clear the box (x,y,w,h)
|
|
** txtbox(x,y,w,y,str) - put (str) in a region at (x,y,w,h)
|
|
** putmsg(str) - put (str) in the message area
|
|
** puthelp(str) - put (str) in the upper helpline
|
|
** pad(str,len) - pad (str) to (len) with spaces
|
|
** drawline(row,detail,list,inverse,*dhelp)
|
|
** - draws a line for (*list) at (row) onscreen. If (detail) is
|
|
** nonzero, include port, IRQ and maddr, if (inverse) is nonzero,
|
|
** draw the line in inverse video, and display (*dhelp) on the
|
|
** helpline.
|
|
** drawlist(row,num,detail,list)
|
|
** - draw (num) entries from (list) at (row) onscreen, passile (detail)
|
|
** through to drawline().
|
|
** showparams(dev) - displays the relevant parameters for (dev) below the lists onscreen.
|
|
** yesno(str) - displays (str) in the message area, and returns nonzero on 'y' or 'Y'
|
|
** redraw(); - Redraws the entire screen layout, including the
|
|
** - two list panels.
|
|
**/
|
|
|
|
/**
|
|
** putxy
|
|
** writes (str) at x,y onscreen
|
|
** putxyl
|
|
** writes up to (len) of (str) at x,y onscreen.
|
|
**
|
|
** Supports embedded formatting :
|
|
** !i - inverse mode.
|
|
** !b - bold mode.
|
|
** !n - normal mode.
|
|
**/
|
|
static void
|
|
putxyl(int x, int y, char *str, int len)
|
|
{
|
|
move(x,y);
|
|
normal();
|
|
|
|
while((*str) && (len--))
|
|
{
|
|
if (*str == '!') /* format escape? */
|
|
{
|
|
switch(*(str+1)) /* depending on the next character */
|
|
{
|
|
case 'i':
|
|
inverse();
|
|
str +=2; /* skip formatting */
|
|
len++; /* doesn't count for length */
|
|
break;
|
|
|
|
case 'b':
|
|
bold();
|
|
str +=2; /* skip formatting */
|
|
len++; /* doesn't count for length */
|
|
break;
|
|
|
|
case 'n':
|
|
normal();
|
|
str +=2; /* skip formatting */
|
|
len++; /* doesn't count for length */
|
|
break;
|
|
|
|
default:
|
|
putchar(*str++); /* not an escape */
|
|
}
|
|
}else{
|
|
putchar(*str++); /* emit the character */
|
|
}
|
|
}
|
|
}
|
|
|
|
#define putxy(x,y,str) putxyl(x,y,str,-1)
|
|
|
|
|
|
/**
|
|
** erase
|
|
**
|
|
** Erases the region (x,y,w,h)
|
|
**/
|
|
static void
|
|
erase(int x, int y, int w, int h)
|
|
{
|
|
int i;
|
|
|
|
normal();
|
|
for (i = 0; i < h; i++)
|
|
putxyl(x,y++,spaces,w);
|
|
}
|
|
|
|
|
|
/**
|
|
** txtbox
|
|
**
|
|
** Writes (str) into the region (x,y,w,h), supports embedded formatting using
|
|
** putxy. Lines are not wrapped, newlines must be forced with \n.
|
|
**/
|
|
static void
|
|
txtbox(int x, int y, int w, int h, char *str)
|
|
{
|
|
int i = 0;
|
|
|
|
h--;
|
|
while((str[i]) && h)
|
|
{
|
|
if (str[i] == '\n') /* newline */
|
|
{
|
|
putxyl(x,y,str,(i<w)?i:w); /* write lesser of i or w */
|
|
y++; /* move down */
|
|
h--; /* room for one less */
|
|
str += (i+1); /* skip first newline */
|
|
i = 0; /* zero offset */
|
|
}else{
|
|
i++; /* next character */
|
|
}
|
|
}
|
|
if (h) /* end of string, not region */
|
|
putxyl(x,y,str,w);
|
|
}
|
|
|
|
|
|
/**
|
|
** putmsg
|
|
**
|
|
** writes (msg) in the helptext area
|
|
**/
|
|
static void
|
|
putmsg(char *msg)
|
|
{
|
|
erase(0,18,80,3); /* clear area */
|
|
txtbox(0,18,80,3,msg);
|
|
}
|
|
|
|
|
|
/**
|
|
** puthelp
|
|
**
|
|
** Writes (msg) in the helpline area
|
|
**/
|
|
static void
|
|
puthelp(char *msg)
|
|
{
|
|
erase(0,22,80,1);
|
|
putxy(0,22,msg);
|
|
}
|
|
|
|
|
|
/**
|
|
** masterhelp
|
|
**
|
|
** Draws the help message at the bottom of the screen
|
|
**/
|
|
static void
|
|
masterhelp(char *msg)
|
|
{
|
|
erase(0,23,80,1);
|
|
putxy(0,23,msg);
|
|
}
|
|
|
|
|
|
/**
|
|
** pad
|
|
**
|
|
** space-pads a (str) to (len) characters
|
|
**/
|
|
static void
|
|
pad(char *str, int len)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; str[i]; i++) /* find the end of the string */
|
|
;
|
|
if (i >= len) /* no padding needed */
|
|
return;
|
|
while(i < len) /* pad */
|
|
str[i++] = ' ';
|
|
str[i] = '\0';
|
|
}
|
|
|
|
|
|
/**
|
|
** drawline
|
|
**
|
|
** Displays entry (ofs) of (list) in region at (row) onscreen, optionally displaying
|
|
** the port and IRQ fields if (detail) is nonzero. If (inverse), in inverse video.
|
|
**
|
|
** The text (dhelp) is displayed if the item is a normal device, otherwise
|
|
** help is shown for normal or zoomed comments
|
|
**/
|
|
static void
|
|
drawline(int row, int detail, DEV_LIST *list, int inverse, char *dhelp)
|
|
{
|
|
char lbuf[90],nb[70],db[20],ib[16],pb[16];
|
|
|
|
if (list->comment == DEV_DEVICE)
|
|
{
|
|
nb[0] = ' ';
|
|
strncpy(nb+1,list->name,57);
|
|
}else{
|
|
strncpy(nb,list->name,58);
|
|
if ((list->comment == DEV_ZOOMED) && (list->next))
|
|
if (list->next->comment == DEV_DEVICE) /* only mention if there's something hidden */
|
|
strcat(nb," (Collapsed)");
|
|
}
|
|
nb[58] = '\0';
|
|
pad(nb,60);
|
|
if (list->conflicts) /* device in conflict? */
|
|
{
|
|
if (inverse)
|
|
{
|
|
strcpy(nb+54," !nCONF!i "); /* tag conflict, careful of length */
|
|
}else{
|
|
strcpy(nb+54," !iCONF!n "); /* tag conflict, careful of length */
|
|
}
|
|
}
|
|
if (list->comment == DEV_DEVICE)
|
|
{
|
|
sprintf(db,"%s%d",list->dev,list->unit);
|
|
pad(db,8);
|
|
}else{
|
|
strcpy(db," ");
|
|
}
|
|
if ((list->irq > 0) && detail && (list->comment == DEV_DEVICE))
|
|
{
|
|
sprintf(ib," %d",list->irq);
|
|
pad(ib,4);
|
|
}else{
|
|
strcpy(ib," ");
|
|
}
|
|
if ((list->iobase > 0) && detail && (list->comment == DEV_DEVICE))
|
|
{
|
|
sprintf(pb,"0x%x",list->iobase);
|
|
pad(pb,7);
|
|
}else{
|
|
strcpy(pb," ");
|
|
}
|
|
|
|
sprintf(lbuf," %s%s%s%s%s",inverse?"!i":"",nb,db,ib,pb);
|
|
|
|
putxyl(0,row,lbuf,80);
|
|
if (dhelp)
|
|
{
|
|
switch(list->comment)
|
|
{
|
|
case DEV_DEVICE: /* ordinary device */
|
|
puthelp(dhelp);
|
|
break;
|
|
case DEV_COMMENT:
|
|
puthelp("");
|
|
if (list->next)
|
|
if (list->next->comment == DEV_DEVICE)
|
|
puthelp(" [!bEnter!n] Collapse device list [!bC!n] Collapse all lists");
|
|
break;
|
|
case DEV_ZOOMED:
|
|
puthelp("");
|
|
if (list->next)
|
|
if (list->next->comment == DEV_DEVICE)
|
|
puthelp(" [!bEnter!n] Expand device list [!bX!n] Expand all lists");
|
|
break;
|
|
default:
|
|
puthelp(" WARNING: This list entry corrupted!");
|
|
break;
|
|
}
|
|
}
|
|
move(0,row); /* put the cursor somewhere relevant */
|
|
}
|
|
|
|
|
|
/**
|
|
** drawlist
|
|
**
|
|
** Displays (num) lines of the contents of (list) at (row), optionally displaying the
|
|
** port and IRQ fields as well if (detail) is nonzero
|
|
**
|
|
** printf in the kernel is essentially useless, so we do most of the hard work ourselves here.
|
|
**/
|
|
static void
|
|
drawlist(int row, int num, int detail, DEV_LIST *list)
|
|
{
|
|
int ofs;
|
|
|
|
for(ofs = 0; ofs < num; ofs++)
|
|
{
|
|
if (list)
|
|
{
|
|
drawline(row+ofs,detail,list,0,NULL); /* NULL -> don't draw empty help string */
|
|
list = nextent(list); /* move down visible list */
|
|
}else{
|
|
erase(0,row+ofs,80,1);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
** redrawactive
|
|
**
|
|
** Redraws the active list
|
|
**/
|
|
static void
|
|
redrawactive(void)
|
|
{
|
|
char cbuf[16];
|
|
|
|
if (conflicts)
|
|
{
|
|
sprintf(cbuf,"!i%d conflict%s-",conflicts,(conflicts>1)?"s":"");
|
|
putxy(45,0,cbuf);
|
|
}else{
|
|
putxyl(45,0,lines,16);
|
|
}
|
|
drawlist(1,8,1,alist); /* draw device lists */
|
|
}
|
|
|
|
/**
|
|
** redrawinactive
|
|
**
|
|
** Redraws the inactive list
|
|
**/
|
|
static void
|
|
redrawinactive(void)
|
|
{
|
|
drawlist(10,7,0,ilist); /* draw device lists */
|
|
}
|
|
|
|
|
|
/**
|
|
** redraw
|
|
**
|
|
** Clear the screen and redraw the entire layout
|
|
**/
|
|
static void
|
|
redraw(void)
|
|
{
|
|
clear();
|
|
putxy(0,0,lines);
|
|
putxy(3,0,"!bActive!n-!bDrivers");
|
|
putxy(63,0,"!bDev!n---!bIRQ!n--!bPort");
|
|
putxy(0,9,lines);
|
|
putxy(3,9,"!bInactive!n-!bDrivers");
|
|
putxy(63,9,"!bDev");
|
|
putxy(0,17,lines);
|
|
putxy(0,21,lines);
|
|
masterhelp(" [!bTAB!n] Change fields [!bQ!n] Save and Exit [!b?!n] Help");
|
|
|
|
redrawactive();
|
|
redrawinactive();
|
|
}
|
|
|
|
|
|
/**
|
|
** yesnocancel
|
|
**
|
|
** Put (str) in the message area, and return 1 if the user hits 'y' or 'Y',
|
|
** 2 if they hit 'c' or 'C', or 0 for 'n' or 'N'.
|
|
**/
|
|
static int
|
|
yesnocancel(char *str)
|
|
{
|
|
|
|
putmsg(str);
|
|
for(;;)
|
|
switch(getchar())
|
|
{
|
|
case -1:
|
|
case 'n':
|
|
case 'N':
|
|
return(0);
|
|
|
|
case 'y':
|
|
case 'Y':
|
|
return(1);
|
|
|
|
case 'c':
|
|
case 'C':
|
|
return(2);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
** showparams
|
|
**
|
|
** Show device parameters in the region below the lists
|
|
**
|
|
** 0 5 10 15 20 25 30 35 40 45 50 55 60 67 70 75
|
|
** |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|....
|
|
** +--------------------------------------------------------------------------------+
|
|
** 17-|--------------------------------------------------------------------------------|
|
|
** 18-| Port address : 0x0000 Memory address : 0x00000 Conflict allowed |
|
|
** 19-| IRQ number : 00 Memory size : 0x0000 |
|
|
** 20-| Flags : 0x0000 DRQ number : 00 |
|
|
** 21-|--------------------------------------------------------------------------------|
|
|
**/
|
|
static void
|
|
showparams(DEV_LIST *dev)
|
|
{
|
|
char buf[80];
|
|
|
|
erase(0,18,80,3); /* clear area */
|
|
if (!dev)
|
|
return;
|
|
if (dev->comment != DEV_DEVICE)
|
|
return;
|
|
|
|
|
|
if (dev->iobase > 0)
|
|
{
|
|
sprintf(buf,"Port address : 0x%x",dev->iobase);
|
|
putxy(1,18,buf);
|
|
}
|
|
|
|
if (dev->irq > 0)
|
|
{
|
|
sprintf(buf,"IRQ number : %d",dev->irq);
|
|
putxy(1,19,buf);
|
|
}
|
|
sprintf(buf,"Flags : 0x%x",dev->flags);
|
|
putxy(1,20,buf);
|
|
if (dev->maddr > 0)
|
|
{
|
|
sprintf(buf,"Memory address : 0x%x",dev->maddr);
|
|
putxy(26,18,buf);
|
|
}
|
|
if (dev->msize > 0)
|
|
{
|
|
sprintf(buf,"Memory size : 0x%x",dev->msize);
|
|
putxy(26,19,buf);
|
|
}
|
|
|
|
if (dev->drq > 0)
|
|
{
|
|
sprintf(buf,"DRQ number : %d",dev->drq);
|
|
putxy(26,20,buf);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
** Editing functions for device parameters
|
|
**
|
|
** editval(x,y,width,hex,min,max,val) - Edit (*val) in a field (width) wide at (x,y)
|
|
** onscreen. Refuse values outsise (min) and (max).
|
|
** editparams(dev) - Edit the parameters for (dev)
|
|
**/
|
|
|
|
|
|
#define VetRet(code) \
|
|
{ \
|
|
if ((i >= min) && (i <= max)) /* legit? */ \
|
|
{ \
|
|
*val = i; \
|
|
sprintf(buf,hex?"0x%x":"%d",i); \
|
|
putxy(hex?x-2:x,y,buf); \
|
|
return(code); /* all done and exit */ \
|
|
} \
|
|
i = *val; /* restore original value */ \
|
|
delta = 1; /* restore other stuff */ \
|
|
}
|
|
|
|
|
|
/**
|
|
** editval
|
|
**
|
|
** Edit (*val) at (x,y) in (hex)?hex:decimal mode, allowing values between (min) and (max)
|
|
** in a field (width) wide. (Allow one space)
|
|
** If (ro) is set, we're in "readonly" mode, so disallow edits.
|
|
**
|
|
** Return KEY_TAB on \t, KEY_EXIT on 'q'
|
|
**/
|
|
static int
|
|
editval(int x, int y, int width, int hex, int min, int max, int *val, int ro)
|
|
{
|
|
int i = *val; /* work with copy of the value */
|
|
char buf[2+11+1],tc[11+1]; /* display buffer, text copy */
|
|
int xp = 0; /* cursor offset into text copy */
|
|
int delta = 1; /* force redraw first time in */
|
|
int c;
|
|
int extended = 0; /* stage counter for extended key sequences */
|
|
|
|
if (hex) /* we presume there's a leading 0x onscreen */
|
|
putxy(x-2,y,"!i0x"); /* coz there sure is now */
|
|
|
|
for (;;)
|
|
{
|
|
if (delta) /* only update if necessary */
|
|
{
|
|
sprintf(tc,hex?"%x":"%d",i); /* make a text copy of the value */
|
|
sprintf(buf,"!i%s",tc); /* format for printing */
|
|
erase(x,y,width,1); /* clear the area */
|
|
putxy(x,y,buf); /* write */
|
|
xp = strlen(tc); /* cursor always at end */
|
|
move(x+xp,y); /* position the cursor */
|
|
}
|
|
|
|
c = getchar();
|
|
|
|
switch(extended) /* escape handling */
|
|
{
|
|
case 0:
|
|
if (c == 0x1b) /* esc? */
|
|
{
|
|
extended = 1; /* flag and spin */
|
|
continue;
|
|
}
|
|
extended = 0;
|
|
break; /* nope, drop through */
|
|
|
|
case 1: /* there was an escape prefix */
|
|
if (c == '[' || c == 'O') /* second character in sequence */
|
|
{
|
|
extended = 2;
|
|
continue;
|
|
}
|
|
if (c == 0x1b)
|
|
return(KEY_EXIT); /* double esc exits */
|
|
extended = 0;
|
|
break; /* nup, not a sequence. */
|
|
|
|
case 2:
|
|
extended = 0;
|
|
switch(c) /* looks like the real McCoy */
|
|
{
|
|
case 'A':
|
|
VetRet(KEY_UP); /* leave if OK */
|
|
continue;
|
|
case 'B':
|
|
VetRet(KEY_DOWN); /* leave if OK */
|
|
continue;
|
|
case 'C':
|
|
VetRet(KEY_RIGHT); /* leave if OK */
|
|
continue;
|
|
case 'D':
|
|
VetRet(KEY_LEFT); /* leave if OK */
|
|
continue;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
}
|
|
|
|
switch(c)
|
|
{
|
|
case '\t': /* trying to tab off */
|
|
VetRet(KEY_TAB); /* verify and maybe return */
|
|
break;
|
|
|
|
case -1:
|
|
case 'q':
|
|
case 'Q':
|
|
VetRet(KEY_EXIT);
|
|
break;
|
|
|
|
case '\b':
|
|
case '\177': /* BS or DEL */
|
|
if (ro) /* readonly? */
|
|
{
|
|
puthelp(" !iThis value cannot be edited (Press ESC)");
|
|
while(getchar() != 0x1b); /* wait for key */
|
|
return(KEY_NULL); /* spin */
|
|
}
|
|
if (xp) /* still something left to delete */
|
|
{
|
|
i = (hex ? i/0x10u : i/10); /* strip last digit */
|
|
delta = 1; /* force update */
|
|
}
|
|
break;
|
|
|
|
case 588:
|
|
VetRet(KEY_UP);
|
|
break;
|
|
|
|
case '\r':
|
|
case '\n':
|
|
case 596:
|
|
VetRet(KEY_DOWN);
|
|
break;
|
|
|
|
case 591:
|
|
VetRet(KEY_LEFT);
|
|
break;
|
|
|
|
case 593:
|
|
VetRet(KEY_RIGHT);
|
|
break;
|
|
|
|
default:
|
|
if (ro) /* readonly? */
|
|
{
|
|
puthelp(" !iThis value cannot be edited (Press ESC)");
|
|
while(getchar() != 0x1b); /* wait for key */
|
|
return(KEY_NULL); /* spin */
|
|
}
|
|
if (xp >= width) /* no room for more characters anyway */
|
|
break;
|
|
if (hex)
|
|
{
|
|
if ((c >= '0') && (c <= '9'))
|
|
{
|
|
i = i*0x10 + (c-'0'); /* update value */
|
|
delta = 1;
|
|
break;
|
|
}
|
|
if ((c >= 'a') && (c <= 'f'))
|
|
{
|
|
i = i*0x10 + (c-'a'+0xa);
|
|
delta = 1;
|
|
break;
|
|
}
|
|
if ((c >= 'A') && (c <= 'F'))
|
|
{
|
|
i = i*0x10 + (c-'A'+0xa);
|
|
delta = 1;
|
|
break;
|
|
}
|
|
}else{
|
|
if ((c >= '0') && (c <= '9'))
|
|
{
|
|
i = i*10 + (c-'0'); /* update value */
|
|
delta = 1; /* force redraw */
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
** editparams
|
|
**
|
|
** Edit the parameters for (dev)
|
|
**
|
|
** Note that it's _always_ possible to edit the flags, otherwise it might be
|
|
** possible for this to spin in an endless loop...
|
|
** 0 5 10 15 20 25 30 35 40 45 50 55 60 67 70 75
|
|
** |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|....
|
|
** +--------------------------------------------------------------------------------+
|
|
** 17-|--------------------------------------------------------------------------------|
|
|
** 18-| Port address : 0x0000 Memory address : 0x00000 Conflict allowed |
|
|
** 19-| IRQ number : 00 Memory size : 0x0000 |
|
|
** 20-| Flags : 0x0000 DRQ number : 00 |
|
|
** 21-|--------------------------------------------------------------------------------|
|
|
**
|
|
** The "intelligence" in this function that hops around based on the directional
|
|
** returns from editval isn't very smart, and depends on the layout above.
|
|
**/
|
|
static void
|
|
editparams(DEV_LIST *dev)
|
|
{
|
|
int ret;
|
|
char buf[16]; /* needs to fit the device name */
|
|
|
|
putxy(2,17,"!bParameters!n-!bfor!n-!bdevice!n-");
|
|
sprintf(buf,"!b%s",dev->dev);
|
|
putxy(24,17,buf);
|
|
|
|
erase(1,22,80,1);
|
|
for (;;)
|
|
{
|
|
ep_iobase:
|
|
if (dev->iobase > 0)
|
|
{
|
|
puthelp(" IO Port address (Hexadecimal, 0x1-0xffff)");
|
|
ret = editval(18,18,5,1,0x1,0xffff,&(dev->iobase),(dev->attrib & FLG_FIXIOBASE));
|
|
switch(ret)
|
|
{
|
|
case KEY_EXIT:
|
|
goto ep_exit;
|
|
|
|
case KEY_RIGHT:
|
|
if (dev->maddr > 0)
|
|
goto ep_maddr;
|
|
break;
|
|
|
|
case KEY_TAB:
|
|
case KEY_DOWN:
|
|
goto ep_irq;
|
|
}
|
|
goto ep_iobase;
|
|
}
|
|
ep_irq:
|
|
if (dev->irq > 0)
|
|
{
|
|
puthelp(" Interrupt number (Decimal, 1-15)");
|
|
ret = editval(16,19,3,0,1,15,&(dev->irq),(dev->attrib & FLG_FIXIRQ));
|
|
switch(ret)
|
|
{
|
|
case KEY_EXIT:
|
|
goto ep_exit;
|
|
|
|
case KEY_RIGHT:
|
|
if (dev->msize > 0)
|
|
goto ep_msize;
|
|
break;
|
|
|
|
case KEY_UP:
|
|
if (dev->iobase > 0)
|
|
goto ep_iobase;
|
|
break;
|
|
|
|
case KEY_TAB:
|
|
case KEY_DOWN:
|
|
goto ep_flags;
|
|
}
|
|
goto ep_irq;
|
|
}
|
|
ep_flags:
|
|
puthelp(" Device-specific flag values.");
|
|
ret = editval(18,20,8,1,INT_MIN,INT_MAX,&(dev->flags),0);
|
|
switch(ret)
|
|
{
|
|
case KEY_EXIT:
|
|
goto ep_exit;
|
|
|
|
case KEY_RIGHT:
|
|
if (dev->drq > 0)
|
|
goto ep_drq;
|
|
break;
|
|
|
|
case KEY_UP:
|
|
if (dev->irq > 0)
|
|
goto ep_irq;
|
|
if (dev->iobase > 0)
|
|
goto ep_iobase;
|
|
break;
|
|
|
|
case KEY_DOWN:
|
|
if (dev->maddr > 0)
|
|
goto ep_maddr;
|
|
if (dev->msize > 0)
|
|
goto ep_msize;
|
|
if (dev->drq > 0)
|
|
goto ep_drq;
|
|
break;
|
|
|
|
case KEY_TAB:
|
|
goto ep_maddr;
|
|
}
|
|
goto ep_flags;
|
|
ep_maddr:
|
|
if (dev->maddr > 0)
|
|
{
|
|
puthelp(" Device memory start address (Hexadecimal, 0x1-0xfffff)");
|
|
ret = editval(45,18,6,1,0x1,0xfffff,&(dev->maddr),(dev->attrib & FLG_FIXMADDR));
|
|
switch(ret)
|
|
{
|
|
case KEY_EXIT:
|
|
goto ep_exit;
|
|
|
|
case KEY_LEFT:
|
|
if (dev->iobase > 0)
|
|
goto ep_iobase;
|
|
break;
|
|
|
|
case KEY_UP:
|
|
goto ep_flags;
|
|
|
|
case KEY_DOWN:
|
|
if (dev->msize > 0)
|
|
goto ep_msize;
|
|
if (dev->drq > 0)
|
|
goto ep_drq;
|
|
break;
|
|
|
|
case KEY_TAB:
|
|
goto ep_msize;
|
|
}
|
|
goto ep_maddr;
|
|
}
|
|
ep_msize:
|
|
if (dev->msize > 0)
|
|
{
|
|
puthelp(" Device memory size (Hexadecimal, 0x1-0x10000)");
|
|
ret = editval(45,19,5,1,0x1,0x10000,&(dev->msize),(dev->attrib & FLG_FIXMSIZE));
|
|
switch(ret)
|
|
{
|
|
case KEY_EXIT:
|
|
goto ep_exit;
|
|
|
|
case KEY_LEFT:
|
|
if (dev->irq > 0)
|
|
goto ep_irq;
|
|
break;
|
|
|
|
case KEY_UP:
|
|
if (dev->maddr > 0)
|
|
goto ep_maddr;
|
|
goto ep_flags;
|
|
|
|
case KEY_DOWN:
|
|
if (dev->drq > 0)
|
|
goto ep_drq;
|
|
break;
|
|
|
|
case KEY_TAB:
|
|
goto ep_drq;
|
|
}
|
|
goto ep_msize;
|
|
}
|
|
ep_drq:
|
|
if (dev->drq > 0)
|
|
{
|
|
puthelp(" Device DMA request number (Decimal, 1-7)");
|
|
ret = editval(43,20,2,0,1,7,&(dev->drq),(dev->attrib & FLG_FIXDRQ));
|
|
switch(ret)
|
|
{
|
|
case KEY_EXIT:
|
|
goto ep_exit;
|
|
|
|
case KEY_LEFT:
|
|
goto ep_flags;
|
|
|
|
case KEY_UP:
|
|
if (dev->msize > 0)
|
|
goto ep_msize;
|
|
if (dev->maddr > 0)
|
|
goto ep_maddr;
|
|
goto ep_flags;
|
|
|
|
case KEY_TAB:
|
|
goto ep_iobase;
|
|
}
|
|
goto ep_drq;
|
|
}
|
|
}
|
|
ep_exit:
|
|
dev->changed = 1; /* mark as changed */
|
|
}
|
|
|
|
static char *helptext[] =
|
|
{
|
|
" Using the UserConfig kernel settings editor",
|
|
" -------------------------------------------",
|
|
"",
|
|
"VISUAL MODE:",
|
|
"",
|
|
"- - Layout -",
|
|
"",
|
|
"The screen displays a list of available drivers, divided into two",
|
|
"scrolling lists: Active Drivers, and Inactive Drivers. Each list is",
|
|
"by default collapsed and can be expanded to show all the drivers",
|
|
"available in each category. The parameters for the currently selected",
|
|
"driver are shown at the bottom of the screen.",
|
|
"",
|
|
"- - Moving around -",
|
|
"",
|
|
"To move in the current list, use the UP and DOWN cursor keys to select",
|
|
"an item (the selected item will be highlighted). If the item is a",
|
|
"category name, you may alternatively expand or collapse the list of",
|
|
"drivers for that category by pressing [!bENTER!n]. Once the category is",
|
|
"expanded, you can select each driver in the same manner and either:",
|
|
"",
|
|
" - change its parameters using [!bENTER!n]",
|
|
" - move it to the Inactive list using [!bDEL!n]",
|
|
"",
|
|
"Use the [!bTAB!n] key to toggle between the Active and Inactive list; if",
|
|
"you need to move a driver from the Inactive list back to the Active",
|
|
"one, select it in the Inactive list, using [!bTAB!n] to change lists if",
|
|
"necessary, and press [!bENTER!n] -- the device will be moved back to",
|
|
"its place in the Active list.",
|
|
"",
|
|
"- - Altering the list/parameters -",
|
|
"",
|
|
"Any drivers for devices not installed in your system should be moved",
|
|
"to the Inactive list, until there are no remaining parameter conflicts",
|
|
"between the drivers, as indicated at the top.",
|
|
"",
|
|
"Once the list of Active drivers only contains entries for the devices",
|
|
"present in your system, you can set their parameters (Interrupt, DMA",
|
|
"channel, I/O addresses). To do this, select the driver and press",
|
|
"[!bENTER!n]: it is now possible to edit the settings at the",
|
|
"bottom of the screen. Use [!bTAB!n] to change fields, and when you are",
|
|
"finished, use [!bQ!n] to return to the list.",
|
|
"",
|
|
"- - Saving changes -",
|
|
"",
|
|
"When all settings seem correct, and you wish to proceed with the",
|
|
"kernel device probing and boot, press [!bQ!n] -- you will be asked to",
|
|
"confirm your choice.",
|
|
"",
|
|
NULL
|
|
};
|
|
|
|
|
|
/**
|
|
** helpscreen
|
|
**
|
|
** Displays help text onscreen for people that are confused, using a simple
|
|
** pager.
|
|
**/
|
|
static void
|
|
helpscreen(void)
|
|
{
|
|
int topline = 0; /* where we are in the text */
|
|
int line = 0; /* last line we displayed */
|
|
int c, delta = 1;
|
|
char prompt[80];
|
|
|
|
for (;;) /* loop until user quits */
|
|
{
|
|
/* display help text */
|
|
if (delta)
|
|
{
|
|
clear(); /* remove everything else */
|
|
for (line = topline;
|
|
(line < (topline + 24)) && (helptext[line]);
|
|
line++)
|
|
putxy(0,line-topline,helptext[line]);
|
|
delta = 0;
|
|
}
|
|
|
|
/* prompt */
|
|
sprintf(prompt,"!i --%s-- [U]p [D]own [Q]uit !n",helptext[line] ? "MORE" : "END");
|
|
putxy(0,24,prompt);
|
|
|
|
c = getchar(); /* so what do they say? */
|
|
|
|
switch (c)
|
|
{
|
|
case 'u':
|
|
case 'U':
|
|
case 'b':
|
|
case 'B': /* wired into 'more' users' fingers */
|
|
if (topline > 0) /* room to go up? */
|
|
{
|
|
topline -= 24;
|
|
if (topline < 0) /* don't go too far */
|
|
topline = 0;
|
|
delta = 1;
|
|
}
|
|
break;
|
|
|
|
case 'd':
|
|
case 'D':
|
|
case ' ': /* expected by most people */
|
|
if (helptext[line]) /* maybe more below? */
|
|
{
|
|
topline += 24;
|
|
delta = 1;
|
|
}
|
|
break;
|
|
|
|
case 'q':
|
|
case 'Q':
|
|
redraw(); /* restore the screen */
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
** High-level control functions
|
|
**/
|
|
|
|
|
|
/**
|
|
** dolist
|
|
**
|
|
** Handle user movement within (*list) in the region starting at (row) onscreen with
|
|
** (num) lines, starting at (*ofs) offset from row onscreen.
|
|
** Pass (detail) on to drawing routines.
|
|
**
|
|
** If the user hits a key other than a cursor key, maybe return a code.
|
|
**
|
|
** (*list) points to the device at the top line in the region, (*ofs) is the
|
|
** position of the highlight within the region. All routines below
|
|
** this take only a device and an absolute row : use ofsent() to find the
|
|
** device, and add (*ofs) to (row) to find the absolute row.
|
|
**/
|
|
static int
|
|
dolist(int row, int num, int detail, int *ofs, DEV_LIST **list, char *dhelp)
|
|
{
|
|
int extended = 0;
|
|
int c;
|
|
DEV_LIST *lp;
|
|
int delta = 1;
|
|
|
|
for(;;)
|
|
{
|
|
if (delta)
|
|
{
|
|
showparams(ofsent(*ofs,*list)); /* show device parameters */
|
|
drawline(row+*ofs,detail,ofsent(*ofs,*list),1,dhelp); /* highlight current line */
|
|
delta = 0;
|
|
}
|
|
|
|
c = getchar(); /* get a character */
|
|
if ((extended == 2) || (c==588) || (c==596)) /* console gives "alternative" codes */
|
|
{
|
|
extended = 0; /* no longer */
|
|
switch(c)
|
|
{
|
|
case 588: /* syscons' idea of 'up' */
|
|
case 'A': /* up */
|
|
if (*ofs) /* just a move onscreen */
|
|
{
|
|
drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp);/* unhighlight current line */
|
|
(*ofs)--; /* move up */
|
|
}else{
|
|
lp = prevent(*list); /* can we go up? */
|
|
if (!lp) /* no */
|
|
break;
|
|
*list = lp; /* yes, move up list */
|
|
drawlist(row,num,detail,*list);
|
|
}
|
|
delta = 1;
|
|
break;
|
|
|
|
case 596: /* dooby-do */
|
|
case 'B': /* down */
|
|
lp = ofsent(*ofs,*list); /* get current item */
|
|
if (!nextent(lp))
|
|
break; /* nothing more to move to */
|
|
drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp); /* unhighlight current line */
|
|
if (*ofs < (num-1)) /* room to move onscreen? */
|
|
{
|
|
(*ofs)++;
|
|
}else{
|
|
*list = nextent(*list); /* scroll region down */
|
|
drawlist(row,num,detail,*list);
|
|
}
|
|
delta = 1;
|
|
break;
|
|
}
|
|
}else{
|
|
switch(c)
|
|
{
|
|
case '\033':
|
|
extended=1;
|
|
break;
|
|
|
|
case '[': /* cheat : always precedes cursor move */
|
|
case 'O': /* ANSI application key mode */
|
|
if (extended==1)
|
|
extended=2;
|
|
else
|
|
extended=0;
|
|
break;
|
|
|
|
case 'Q':
|
|
case 'q':
|
|
return(KEY_EXIT); /* user requests exit */
|
|
|
|
case '\r':
|
|
case '\n':
|
|
return(KEY_DO); /* "do" something */
|
|
|
|
case '\b':
|
|
case '\177':
|
|
case 599:
|
|
return(KEY_DEL); /* "delete" response */
|
|
|
|
case 'X':
|
|
case 'x':
|
|
return(KEY_UNZOOM); /* expand everything */
|
|
|
|
case 'C':
|
|
case 'c':
|
|
return(KEY_ZOOM); /* collapse everything */
|
|
|
|
case '\t':
|
|
drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp); /* unhighlight current line */
|
|
return(KEY_TAB); /* "move" response */
|
|
|
|
case '\014': /* ^L, redraw */
|
|
return(KEY_REDRAW);
|
|
|
|
case '?': /* helptext */
|
|
return(KEY_HELP);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
** visuserconfig
|
|
**
|
|
** Do the fullscreen config thang
|
|
**/
|
|
static int
|
|
visuserconfig(void)
|
|
{
|
|
int actofs = 0, inactofs = 0, mode = 0, ret = -1, i;
|
|
DEV_LIST *dp;
|
|
|
|
initlist(&active);
|
|
initlist(&inactive);
|
|
alist = active;
|
|
ilist = inactive;
|
|
|
|
getdevs();
|
|
|
|
conflicts = findconflict(active); /* find conflicts in the active list only */
|
|
|
|
redraw();
|
|
|
|
for(;;)
|
|
{
|
|
switch(mode)
|
|
{
|
|
case 0: /* active devices */
|
|
ret = dolist(1,8,1,&actofs,&alist,
|
|
" [!bEnter!n] Edit device parameters [!bDEL!n] Disable device");
|
|
switch(ret)
|
|
{
|
|
case KEY_TAB:
|
|
mode = 1; /* swap lists */
|
|
break;
|
|
|
|
case KEY_REDRAW:
|
|
redraw();
|
|
break;
|
|
|
|
case KEY_ZOOM:
|
|
alist = active;
|
|
actofs = 0;
|
|
expandlist(active);
|
|
redrawactive();
|
|
break;
|
|
|
|
case KEY_UNZOOM:
|
|
alist = active;
|
|
actofs = 0;
|
|
collapselist(active);
|
|
redrawactive();
|
|
break;
|
|
|
|
case KEY_DEL:
|
|
dp = ofsent(actofs,alist); /* get current device */
|
|
if (dp) /* paranoia... */
|
|
{
|
|
if (dp->attrib & FLG_MANDATORY) /* can't be deleted */
|
|
break;
|
|
if (dp == alist) /* moving top item on list? */
|
|
{
|
|
if (dp->next)
|
|
{
|
|
alist = dp->next; /* point list to non-moving item */
|
|
}else{
|
|
alist = dp->prev; /* end of list, go back instead */
|
|
}
|
|
}else{
|
|
if (!dp->next) /* moving last item on list? */
|
|
actofs--;
|
|
}
|
|
dp->conflicts = 0; /* no conflicts on the inactive list */
|
|
movedev(dp,inactive); /* shift to inactive list */
|
|
conflicts = findconflict(active); /* update conflict tags */
|
|
dp->changed = 1;
|
|
redrawactive(); /* redraw */
|
|
redrawinactive();
|
|
}
|
|
break;
|
|
|
|
case KEY_DO: /* edit device parameters */
|
|
dp = ofsent(actofs,alist); /* get current device */
|
|
if (dp) /* paranoia... */
|
|
{
|
|
if (dp->comment == DEV_DEVICE) /* can't edit comments, zoom? */
|
|
{
|
|
masterhelp(" [!bTAB!n] Change fields [!bQ!n] Save device parameters");
|
|
editparams(dp);
|
|
masterhelp(" [!bTAB!n] Change fields [!bQ!n] Save and Exit [!b?!n] Help");
|
|
putxy(0,17,lines);
|
|
conflicts = findconflict(active); /* update conflict tags */
|
|
}else{ /* DO on comment = zoom */
|
|
switch(dp->comment) /* Depends on current state */
|
|
{
|
|
case DEV_COMMENT: /* not currently zoomed */
|
|
dp->comment = DEV_ZOOMED;
|
|
break;
|
|
|
|
case DEV_ZOOMED:
|
|
dp->comment = DEV_COMMENT;
|
|
break;
|
|
}
|
|
}
|
|
redrawactive();
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 1: /* inactive devices */
|
|
ret = dolist(10,7,0,&inactofs,&ilist,
|
|
" [!bEnter!n] Enable device ");
|
|
switch(ret)
|
|
{
|
|
case KEY_TAB:
|
|
mode = 0;
|
|
break;
|
|
|
|
case KEY_REDRAW:
|
|
redraw();
|
|
break;
|
|
|
|
case KEY_ZOOM:
|
|
ilist = inactive;
|
|
inactofs = 0;
|
|
expandlist(inactive);
|
|
redrawinactive();
|
|
break;
|
|
|
|
case KEY_UNZOOM:
|
|
ilist = inactive;
|
|
inactofs = 0;
|
|
collapselist(inactive);
|
|
redrawinactive();
|
|
break;
|
|
|
|
case KEY_DO:
|
|
dp = ofsent(inactofs,ilist); /* get current device */
|
|
if (dp) /* paranoia... */
|
|
{
|
|
if (dp->comment == DEV_DEVICE) /* can't move comments, zoom? */
|
|
{
|
|
if (dp == ilist) /* moving top of list? */
|
|
{
|
|
if (dp->next)
|
|
{
|
|
ilist = dp->next; /* point list to non-moving item */
|
|
}else{
|
|
ilist = dp->prev; /* can't go down, go up instead */
|
|
}
|
|
}else{
|
|
if (!dp->next) /* last entry on list? */
|
|
inactofs--; /* shift cursor up one */
|
|
}
|
|
|
|
movedev(dp,active); /* shift to active list */
|
|
conflicts = findconflict(active); /* update conflict tags */
|
|
dp->changed = 1;
|
|
alist = dp; /* put at top and current */
|
|
actofs = 0;
|
|
while(dp->comment == DEV_DEVICE)
|
|
dp = dp->prev; /* forcibly unzoom section */
|
|
dp ->comment = DEV_COMMENT;
|
|
mode = 0; /* and swap modes to follow it */
|
|
|
|
}else{ /* DO on comment = zoom */
|
|
switch(dp->comment) /* Depends on current state */
|
|
{
|
|
case DEV_COMMENT: /* not currently zoomed */
|
|
dp->comment = DEV_ZOOMED;
|
|
break;
|
|
|
|
case DEV_ZOOMED:
|
|
dp->comment = DEV_COMMENT;
|
|
break;
|
|
}
|
|
}
|
|
redrawactive(); /* redraw */
|
|
redrawinactive();
|
|
}
|
|
break;
|
|
|
|
default: /* nothing else relevant here */
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
mode = 0; /* shouldn't happen... */
|
|
}
|
|
|
|
/* handle returns that are the same for both modes */
|
|
switch (ret) {
|
|
case KEY_HELP:
|
|
helpscreen();
|
|
break;
|
|
|
|
case KEY_EXIT:
|
|
i = yesnocancel(" Save these parameters before exiting? ([!bY!n]es/[!bN!n]o/[!bC!n]ancel) ");
|
|
switch(i)
|
|
{
|
|
case 2: /* cancel */
|
|
redraw();
|
|
break;
|
|
|
|
case 1: /* save and exit */
|
|
savelist(active,1);
|
|
savelist(inactive,0);
|
|
|
|
case 0: /* exit */
|
|
nukelist(active); /* clean up after ourselves */
|
|
nukelist(inactive);
|
|
normal();
|
|
clear();
|
|
return(1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif /* VISUAL_USERCONFIG */
|
|
|
|
/*
|
|
* Copyright (c) 1991 Regents of the University of California.
|
|
* All rights reserved.
|
|
* Copyright (c) 1994 Jordan K. Hubbard
|
|
* All rights reserved.
|
|
* Copyright (c) 1994 David Greenman
|
|
* All rights reserved.
|
|
*
|
|
* Many additional changes by Bruce Evans
|
|
*
|
|
* This code is derived from software contributed by the
|
|
* University of California Berkeley, Jordan K. Hubbard,
|
|
* David Greenman and Bruce Evans.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#define PARM_DEVSPEC 0x1
|
|
#define PARM_INT 0x2
|
|
#define PARM_ADDR 0x3
|
|
#define PARM_STRING 0x4
|
|
|
|
typedef struct _cmdparm {
|
|
int type;
|
|
union {
|
|
struct uc_device *dparm;
|
|
int iparm;
|
|
union {
|
|
void *aparm;
|
|
const char *sparm;
|
|
} u;
|
|
} parm;
|
|
} CmdParm;
|
|
|
|
typedef int (*CmdFunc)(CmdParm *);
|
|
|
|
typedef struct _cmd {
|
|
char *name;
|
|
CmdFunc handler;
|
|
CmdParm *parms;
|
|
} Cmd;
|
|
|
|
|
|
static int lsdevtab(struct uc_device *);
|
|
static struct uc_device *find_device(char *, int);
|
|
static struct uc_device *search_devtable(struct uc_device *, char *, int);
|
|
static void cngets(char *, int);
|
|
static Cmd *parse_cmd(char *);
|
|
static int parse_args(const char *, CmdParm *);
|
|
static int save_dev(struct uc_device *);
|
|
|
|
static int list_devices(CmdParm *);
|
|
static int set_device_ioaddr(CmdParm *);
|
|
static int set_device_irq(CmdParm *);
|
|
static int set_device_drq(CmdParm *);
|
|
static int set_device_iosize(CmdParm *);
|
|
static int set_device_mem(CmdParm *);
|
|
static int set_device_flags(CmdParm *);
|
|
static int set_device_enable(CmdParm *);
|
|
static int set_device_disable(CmdParm *);
|
|
static int quitfunc(CmdParm *);
|
|
static int helpfunc(CmdParm *);
|
|
#if defined(INTRO_USERCONFIG)
|
|
static int introfunc(CmdParm *);
|
|
#endif
|
|
|
|
static int lineno;
|
|
|
|
#ifdef DEV_EISA
|
|
|
|
#include <dev/eisa/eisaconf.h>
|
|
|
|
static int set_num_eisa_slots(CmdParm *);
|
|
|
|
#endif
|
|
|
|
static CmdParm addr_parms[] = {
|
|
{ PARM_DEVSPEC, {} },
|
|
{ PARM_ADDR, {} },
|
|
{ -1, {} },
|
|
};
|
|
|
|
static CmdParm int_parms[] = {
|
|
{ PARM_DEVSPEC, {} },
|
|
{ PARM_INT, {} },
|
|
{ -1, {} },
|
|
};
|
|
|
|
static CmdParm dev_parms[] = {
|
|
{ PARM_DEVSPEC, {} },
|
|
{ -1, {} },
|
|
};
|
|
|
|
#ifdef DEV_EISA
|
|
static CmdParm int_arg[] = {
|
|
{ PARM_INT, {} },
|
|
{ -1, {} },
|
|
};
|
|
#endif
|
|
|
|
static Cmd CmdList[] = {
|
|
{ "?", helpfunc, NULL }, /* ? (help) */
|
|
{ "di", set_device_disable, dev_parms }, /* disable dev */
|
|
{ "dr", set_device_drq, int_parms }, /* drq dev # */
|
|
#ifdef DEV_EISA
|
|
{ "ei", set_num_eisa_slots, int_arg }, /* # EISA slots */
|
|
#endif
|
|
{ "en", set_device_enable, dev_parms }, /* enable dev */
|
|
{ "ex", quitfunc, NULL }, /* exit (quit) */
|
|
{ "f", set_device_flags, int_parms }, /* flags dev mask */
|
|
{ "h", helpfunc, NULL }, /* help */
|
|
#if defined(INTRO_USERCONFIG)
|
|
{ "intro", introfunc, NULL }, /* intro screen */
|
|
#endif
|
|
{ "iom", set_device_mem, addr_parms }, /* iomem dev addr */
|
|
{ "ios", set_device_iosize, int_parms }, /* iosize dev size */
|
|
{ "ir", set_device_irq, int_parms }, /* irq dev # */
|
|
{ "l", list_devices, NULL }, /* ls, list */
|
|
{ "po", set_device_ioaddr, int_parms }, /* port dev addr */
|
|
{ "res", (CmdFunc)cpu_reset, NULL }, /* reset CPU */
|
|
{ "q", quitfunc, NULL }, /* quit */
|
|
#ifdef VISUAL_USERCONFIG
|
|
{ "v", (CmdFunc)visuserconfig, NULL }, /* visual mode */
|
|
#endif
|
|
{ NULL, NULL, NULL },
|
|
};
|
|
|
|
void
|
|
userconfig(void)
|
|
{
|
|
static char banner = 1;
|
|
char input[80];
|
|
int rval;
|
|
Cmd *cmd;
|
|
|
|
load_devtab();
|
|
init_config_script();
|
|
while (1) {
|
|
|
|
/* Only display signon banner if we are about to go interactive */
|
|
if (!has_config_script()) {
|
|
if (!(boothowto & RB_CONFIG))
|
|
#ifdef INTRO_USERCONFIG
|
|
banner = 0;
|
|
#else
|
|
return;
|
|
#endif
|
|
if (banner) {
|
|
banner = 0;
|
|
printf("FreeBSD Kernel Configuration Utility - Version 1.2\n"
|
|
" Type \"help\" for help"
|
|
#ifdef VISUAL_USERCONFIG
|
|
" or \"visual\" to go to the visual\n"
|
|
" configuration interface (requires MGA/VGA display or\n"
|
|
" serial terminal capable of displaying ANSI graphics)"
|
|
#endif
|
|
".\n");
|
|
}
|
|
}
|
|
|
|
printf("config> ");
|
|
cngets(input, 80);
|
|
if (input[0] == '\0')
|
|
continue;
|
|
cmd = parse_cmd(input);
|
|
if (!cmd) {
|
|
printf("Invalid command or syntax. Type `?' for help.\n");
|
|
continue;
|
|
}
|
|
rval = (*cmd->handler)(cmd->parms);
|
|
if (rval) {
|
|
free_devtab();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static Cmd *
|
|
parse_cmd(char *cmd)
|
|
{
|
|
Cmd *cp;
|
|
|
|
for (cp = CmdList; cp->name; cp++) {
|
|
int len = strlen(cp->name);
|
|
|
|
if (!strncmp(cp->name, cmd, len)) {
|
|
while (*cmd && *cmd != ' ' && *cmd != '\t')
|
|
++cmd;
|
|
if (parse_args(cmd, cp->parms))
|
|
return NULL;
|
|
else
|
|
return cp;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
parse_args(const char *cmd, CmdParm *parms)
|
|
{
|
|
while (1) {
|
|
char *ptr;
|
|
|
|
if (*cmd == ' ' || *cmd == '\t') {
|
|
++cmd;
|
|
continue;
|
|
}
|
|
if (parms == NULL || parms->type == -1) {
|
|
if (*cmd == '\0')
|
|
return 0;
|
|
printf("Extra arg(s): %s\n", cmd);
|
|
return 1;
|
|
}
|
|
if (parms->type == PARM_DEVSPEC) {
|
|
int i = 0;
|
|
char devname[64];
|
|
int unit = 0;
|
|
|
|
while (*cmd && !(*cmd == ' ' || *cmd == '\t' ||
|
|
(*cmd >= '0' && *cmd <= '9')))
|
|
devname[i++] = *(cmd++);
|
|
devname[i] = '\0';
|
|
if (*cmd >= '0' && *cmd <= '9') {
|
|
unit = strtoul(cmd, &ptr, 10);
|
|
if (cmd == ptr) {
|
|
printf("Invalid device number\n");
|
|
/* XXX should print invalid token here and elsewhere. */
|
|
return 1;
|
|
}
|
|
/* XXX else should require end of token. */
|
|
cmd = ptr;
|
|
}
|
|
if ((parms->parm.dparm = find_device(devname, unit)) == NULL) {
|
|
printf("No such device: %s%d\n", devname, unit);
|
|
return 1;
|
|
}
|
|
++parms;
|
|
continue;
|
|
}
|
|
if (parms->type == PARM_INT) {
|
|
parms->parm.iparm = strtoul(cmd, &ptr, 0);
|
|
if (cmd == ptr) {
|
|
printf("Invalid numeric argument\n");
|
|
return 1;
|
|
}
|
|
cmd = ptr;
|
|
++parms;
|
|
continue;
|
|
}
|
|
if (parms->type == PARM_ADDR) {
|
|
parms->parm.u.aparm = (void *)(uintptr_t)strtoul(cmd, &ptr, 0);
|
|
if (cmd == ptr) {
|
|
printf("Invalid address argument\n");
|
|
return 1;
|
|
}
|
|
cmd = ptr;
|
|
++parms;
|
|
continue;
|
|
}
|
|
if (parms->type == PARM_STRING) {
|
|
parms->parm.u.sparm = cmd;
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
list_devices(CmdParm *parms)
|
|
{
|
|
lineno = 0;
|
|
if (lsdevtab(uc_devtab)) return 0;
|
|
#ifdef DEV_EISA
|
|
printf("\nNumber of EISA slots to probe: %d\n", num_eisa_slots);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_device_ioaddr(CmdParm *parms)
|
|
{
|
|
parms[0].parm.dparm->id_iobase = parms[1].parm.iparm;
|
|
save_dev(parms[0].parm.dparm);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_device_irq(CmdParm *parms)
|
|
{
|
|
unsigned irq;
|
|
|
|
irq = parms[1].parm.iparm;
|
|
#ifndef PC98
|
|
if (irq == 2) {
|
|
printf("Warning: Remapping IRQ 2 to IRQ 9\n");
|
|
irq = 9;
|
|
}
|
|
else if (irq != -1 && irq > 15) {
|
|
#else
|
|
if (irq != -1 && irq > 15) {
|
|
#endif
|
|
printf("An IRQ > 15 would be invalid.\n");
|
|
return 0;
|
|
}
|
|
parms[0].parm.dparm->id_irq = (irq < 16 ? 1 << irq : 0);
|
|
save_dev(parms[0].parm.dparm);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_device_drq(CmdParm *parms)
|
|
{
|
|
unsigned drq;
|
|
|
|
/*
|
|
* The bounds checking is just to ensure that the value can be printed
|
|
* in 5 characters. 32768 gets converted to -32768 and doesn't fit.
|
|
*/
|
|
drq = parms[1].parm.iparm;
|
|
parms[0].parm.dparm->id_drq = (drq < 32768 ? drq : -1);
|
|
save_dev(parms[0].parm.dparm);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_device_iosize(CmdParm *parms)
|
|
{
|
|
parms[0].parm.dparm->id_msize = parms[1].parm.iparm;
|
|
save_dev(parms[0].parm.dparm);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_device_mem(CmdParm *parms)
|
|
{
|
|
parms[0].parm.dparm->id_maddr = parms[1].parm.u.aparm;
|
|
save_dev(parms[0].parm.dparm);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_device_flags(CmdParm *parms)
|
|
{
|
|
parms[0].parm.dparm->id_flags = parms[1].parm.iparm;
|
|
save_dev(parms[0].parm.dparm);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_device_enable(CmdParm *parms)
|
|
{
|
|
parms[0].parm.dparm->id_enabled = TRUE;
|
|
save_dev(parms[0].parm.dparm);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_device_disable(CmdParm *parms)
|
|
{
|
|
parms[0].parm.dparm->id_enabled = FALSE;
|
|
save_dev(parms[0].parm.dparm);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef DEV_EISA
|
|
static int
|
|
set_num_eisa_slots(CmdParm *parms)
|
|
{
|
|
int num_slots;
|
|
|
|
num_slots = parms[0].parm.iparm;
|
|
num_eisa_slots = (num_slots <= 16 ? num_slots : 10);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
quitfunc(CmdParm *parms)
|
|
{
|
|
/*
|
|
* If kernel config supplied, and we are parsing it, and -c also supplied,
|
|
* ignore a quit command, This provides a safety mechanism to allow
|
|
* recovery from a damaged/buggy kernel config.
|
|
*/
|
|
if ((boothowto & RB_CONFIG) && userconfig_boot_parsing)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
helpfunc(CmdParm *parms)
|
|
{
|
|
printf(
|
|
"Command\t\t\tDescription\n"
|
|
"-------\t\t\t-----------\n"
|
|
"ls\t\t\tList currently configured devices\n"
|
|
"port <devname> <addr>\tSet device port (i/o address)\n"
|
|
"irq <devname> <number>\tSet device irq\n"
|
|
"drq <devname> <number>\tSet device drq\n"
|
|
"iomem <devname> <addr>\tSet device maddr (memory address)\n"
|
|
"iosize <devname> <size>\tSet device memory size\n"
|
|
"flags <devname> <mask>\tSet device flags\n"
|
|
"enable <devname>\tEnable device\n"
|
|
"disable <devname>\tDisable device (will not be probed)\n");
|
|
#ifdef DEV_EISA
|
|
printf("eisa <number>\t\tSet the number of EISA slots to probe\n");
|
|
#endif
|
|
printf(
|
|
"quit\t\t\tExit this configuration utility\n"
|
|
"reset\t\t\tReset CPU\n");
|
|
#ifdef VISUAL_USERCONFIG
|
|
printf("visual\t\t\tGo to fullscreen mode.\n");
|
|
#endif
|
|
printf(
|
|
"help\t\t\tThis message\n\n"
|
|
"Commands may be abbreviated to a unique prefix\n");
|
|
return 0;
|
|
}
|
|
|
|
#if defined(INTRO_USERCONFIG)
|
|
|
|
#if defined (VISUAL_USERCONFIG)
|
|
static void
|
|
center(int y, char *str)
|
|
{
|
|
putxy((80 - strlen(str)) / 2, y, str);
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
introfunc(CmdParm *parms)
|
|
{
|
|
#if defined (VISUAL_USERCONFIG)
|
|
int curr_item, first_time, extended = 0;
|
|
static char *choices[] = {
|
|
" Skip kernel configuration and continue with installation ",
|
|
" Start kernel configuration in full-screen visual mode ",
|
|
" Start kernel configuration in CLI mode ",
|
|
};
|
|
|
|
clear();
|
|
center(2, "!bKernel Configuration Menu!n");
|
|
|
|
curr_item = 0;
|
|
first_time = 1;
|
|
while (1) {
|
|
char tmp[80];
|
|
int c, i;
|
|
|
|
if (!extended) {
|
|
for (i = 0; i < 3; i++) {
|
|
tmp[0] = '\0';
|
|
if (curr_item == i)
|
|
strcpy(tmp, "!i");
|
|
strcat(tmp, choices[i]);
|
|
if (curr_item == i)
|
|
strcat(tmp, "!n");
|
|
putxy(10, 5 + i, tmp);
|
|
}
|
|
|
|
if (first_time) {
|
|
putxy(2, 10, "Here you have the chance to go into kernel configuration mode, making");
|
|
putxy(2, 11, "any changes which may be necessary to properly adjust the kernel to");
|
|
putxy(2, 12, "match your hardware configuration.");
|
|
putxy(2, 14, "If you are installing FreeBSD for the first time, select Visual Mode");
|
|
putxy(2, 15, "(press Down-Arrow then ENTER).");
|
|
putxy(2, 17, "If you need to do more specialized kernel configuration and are an");
|
|
putxy(2, 18, "experienced FreeBSD user, select CLI mode.");
|
|
putxy(2, 20, "If you are !icertain!n that you do not need to configure your kernel");
|
|
putxy(2, 21, "then simply press ENTER or Q now.");
|
|
first_time = 0;
|
|
}
|
|
|
|
move(0, 0); /* move the cursor out of the way */
|
|
}
|
|
c = getchar();
|
|
if ((extended == 2) || (c == 588) || (c == 596)) { /* console gives "alternative" codes */
|
|
extended = 0; /* no longer */
|
|
switch (c) {
|
|
case 588:
|
|
case 'A': /* up */
|
|
if (curr_item > 0)
|
|
--curr_item;
|
|
break;
|
|
|
|
case 596:
|
|
case 'B': /* down */
|
|
if (curr_item < 2)
|
|
++curr_item;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
switch(c) {
|
|
case '\033':
|
|
extended = 1;
|
|
break;
|
|
|
|
case '[': /* cheat : always precedes cursor move */
|
|
case 'O': /* ANSI application key mode */
|
|
if (extended == 1)
|
|
extended = 2;
|
|
else
|
|
extended = 0;
|
|
break;
|
|
|
|
case -1:
|
|
case 'Q':
|
|
case 'q':
|
|
clear();
|
|
return 1; /* user requests exit */
|
|
|
|
case '1': /* select an item */
|
|
case 'S':
|
|
case 's':
|
|
curr_item = 0;
|
|
break;
|
|
case '2':
|
|
case 'V':
|
|
case 'v':
|
|
curr_item = 1;
|
|
break;
|
|
case '3':
|
|
case 'C':
|
|
case 'c':
|
|
curr_item = 2;
|
|
break;
|
|
|
|
case 'U': /* up */
|
|
case 'u':
|
|
case 'P':
|
|
case 'p':
|
|
if (curr_item > 0)
|
|
--curr_item;
|
|
break;
|
|
|
|
case 'D': /* down */
|
|
case 'd':
|
|
case 'N':
|
|
case 'n':
|
|
if (curr_item < 2)
|
|
++curr_item;
|
|
break;
|
|
|
|
case '\r':
|
|
case '\n':
|
|
clear();
|
|
if (!curr_item)
|
|
return 1;
|
|
else if (curr_item == 1)
|
|
return visuserconfig();
|
|
else {
|
|
putxy(0, 1, "Type \"help\" for help or \"quit\" to exit.");
|
|
/* enable quitfunc */
|
|
userconfig_boot_parsing=0;
|
|
move (0, 3);
|
|
boothowto |= RB_CONFIG; /* force -c */
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
lsdevtab(struct uc_device *dt)
|
|
{
|
|
for (; dt->id_id != 0; dt++) {
|
|
char dname[80];
|
|
|
|
if (lineno >= 23) {
|
|
printf("<More> ");
|
|
if (!userconfig_boot_parsing) {
|
|
if (getchar() == 'q') {
|
|
printf("quit\n");
|
|
return (1);
|
|
}
|
|
printf("\n");
|
|
}
|
|
lineno = 0;
|
|
}
|
|
if (lineno == 0) {
|
|
printf(
|
|
"Device port irq drq iomem iosize unit flags enab\n"
|
|
);
|
|
++lineno;
|
|
}
|
|
sprintf(dname, "%s%d", dt->id_name, dt->id_unit);
|
|
printf("%-9.9s%-#11x%-6d%-6d%-8p%-9d%-6d%-#11x%-5s\n",
|
|
dname, /* dt->id_id, dt->id_driver(by name), */ dt->id_iobase,
|
|
ffs(dt->id_irq) - 1, dt->id_drq, dt->id_maddr, dt->id_msize,
|
|
/* dt->id_intr(by name), */ dt->id_unit, dt->id_flags,
|
|
dt->id_enabled ? "Yes" : "No");
|
|
++lineno;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static void
|
|
load_devtab(void)
|
|
{
|
|
int i, val;
|
|
int count = resource_count();
|
|
int id = 1;
|
|
int dt;
|
|
char *name;
|
|
int unit;
|
|
|
|
uc_devtab = malloc(sizeof(struct uc_device) * (count + 1), M_DEVL,
|
|
M_WAITOK | M_ZERO);
|
|
dt = 0;
|
|
for (i = 0; i < count; i++) {
|
|
name = resource_query_name(i);
|
|
unit = resource_query_unit(i);
|
|
if (unit < 0)
|
|
continue; /* skip wildcards */
|
|
uc_devtab[dt].id_id = id++;
|
|
resource_int_value(name, unit, "port", &uc_devtab[dt].id_iobase);
|
|
val = 0;
|
|
resource_int_value(name, unit, "irq", &val);
|
|
uc_devtab[dt].id_irq = (1 << val);
|
|
resource_int_value(name, unit, "drq", &uc_devtab[dt].id_drq);
|
|
resource_int_value(name, unit, "maddr",(int *)&uc_devtab[dt].id_maddr);
|
|
resource_int_value(name, unit, "msize", &uc_devtab[dt].id_msize);
|
|
uc_devtab[dt].id_unit = unit;
|
|
resource_int_value(name, unit, "flags", &uc_devtab[dt].id_flags);
|
|
val = 0;
|
|
resource_int_value(name, unit, "disabled", &val);
|
|
uc_devtab[dt].id_enabled = !val;
|
|
uc_devtab[dt].id_name = malloc(strlen(name) + 1, M_DEVL,M_WAITOK);
|
|
strcpy(uc_devtab[dt].id_name, name);
|
|
dt++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
free_devtab(void)
|
|
{
|
|
int i;
|
|
int count = resource_count();
|
|
|
|
for (i = 0; i < count; i++)
|
|
if (uc_devtab[i].id_name)
|
|
free(uc_devtab[i].id_name, M_DEVL);
|
|
free(uc_devtab, M_DEVL);
|
|
}
|
|
|
|
static struct uc_device *
|
|
find_device(char *devname, int unit)
|
|
{
|
|
struct uc_device *ret;
|
|
|
|
if ((ret = search_devtable(uc_devtab, devname, unit)) != NULL)
|
|
return ret;
|
|
return NULL;
|
|
}
|
|
|
|
static struct uc_device *
|
|
search_devtable(struct uc_device *dt, char *devname, int unit)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; dt->id_id != 0; dt++)
|
|
if (!strcmp(dt->id_name, devname) && dt->id_unit == unit)
|
|
return dt;
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
cngets(char *input, int maxin)
|
|
{
|
|
int c, nchars = 0;
|
|
|
|
while (1) {
|
|
c = getchar();
|
|
/* Treat ^H or ^? as backspace */
|
|
if ((c == '\010' || c == '\177')) {
|
|
if (nchars) {
|
|
printf("\010 \010");
|
|
*--input = '\0', --nchars;
|
|
}
|
|
continue;
|
|
}
|
|
/* Treat ^U or ^X as kill line */
|
|
else if ((c == '\025' || c == '\030')) {
|
|
while (nchars) {
|
|
printf("\010 \010");
|
|
*--input = '\0', --nchars;
|
|
}
|
|
continue;
|
|
}
|
|
printf("%c", c);
|
|
if ((++nchars == maxin) || (c == '\n') || (c == '\r') || ( c == -1)) {
|
|
*input = '\0';
|
|
break;
|
|
}
|
|
*input++ = (u_char)c;
|
|
}
|
|
}
|
|
|
|
static void
|
|
save_resource(struct uc_device *idev)
|
|
{
|
|
char *name;
|
|
int unit;
|
|
|
|
name = idev->id_name;
|
|
unit = idev->id_unit;
|
|
resource_set_int(name, unit, "port", idev->id_iobase);
|
|
resource_set_int(name, unit, "irq", ffs(idev->id_irq) - 1);
|
|
resource_set_int(name, unit, "drq", idev->id_drq);
|
|
resource_set_int(name, unit, "maddr", (int)idev->id_maddr);
|
|
resource_set_int(name, unit, "msize", idev->id_msize);
|
|
resource_set_int(name, unit, "flags", idev->id_flags);
|
|
resource_set_int(name, unit, "disabled", !idev->id_enabled);
|
|
}
|
|
|
|
static int
|
|
save_dev(idev)
|
|
struct uc_device *idev;
|
|
{
|
|
struct uc_device *id_p,*id_pn;
|
|
char *name = idev->id_name;
|
|
|
|
for (id_p = uc_devlist; id_p; id_p = id_p->id_next) {
|
|
if (id_p->id_id == idev->id_id) {
|
|
id_pn = id_p->id_next;
|
|
if (id_p->id_name)
|
|
free(id_p->id_name, M_DEVL);
|
|
bcopy(idev,id_p,sizeof(struct uc_device));
|
|
save_resource(idev);
|
|
id_p->id_name = malloc(strlen(name)+1, M_DEVL,M_WAITOK);
|
|
strcpy(id_p->id_name, name);
|
|
id_p->id_next = id_pn;
|
|
return 1;
|
|
}
|
|
}
|
|
id_pn = malloc(sizeof(struct uc_device),M_DEVL,M_WAITOK);
|
|
bcopy(idev,id_pn,sizeof(struct uc_device));
|
|
save_resource(idev);
|
|
id_pn->id_name = malloc(strlen(name) + 1, M_DEVL,M_WAITOK);
|
|
strcpy(id_pn->id_name, name);
|
|
id_pn->id_next = uc_devlist;
|
|
uc_devlist = id_pn;
|
|
return 0;
|
|
}
|
|
|
|
|