ftp.nice.ch/pub/next/unix/admin/sysinfo.1.1.0.s.tar.gz#/os-sunos.c

This is os-sunos.c in view mode; [Download] [Up]

/*
 * Copyright (c) 1992 Michael A. Cooper.
 * This software may be freely distributed provided it is not sold for 
 * profit and the author is credited appropriately.
 */

#ifndef lint
static char *RCSid = "$Header: /src/common/usc/bin/sysinfo/RCS/os-sunos.c,v 1.34 1992/11/24 04:05:57 mcooper Exp mcooper $";
#endif

/*
 * $Log: os-sunos.c,v $
 * Revision 1.34  1992/11/24  04:05:57  mcooper
 * New/cleaner KVM/nlist interface.
 *
 * Revision 1.33  1992/11/12  19:12:43  mcooper
 * Add declaration of OpenPROMTraverse().
 *
 * Revision 1.32  1992/08/11  01:33:14  mcooper
 * Adjust format of Error() message.
 *
 * Revision 1.31  1992/07/07  22:25:06  mcooper
 * Add Auspex support from Guy Harris (guy@auspex.com) as well
 * as some cleanup of code format and phrasing of device information:
 * Added an entry for the SPARCengine 1E (6U Eurocard based on the
 * SPARCstation 1; there are also SS2-based and, coming up, SS10-based
 * SPARCengines, although they're not VME cards).
 *
 * Changed the description of the FPA from "Wietek FPA" to "Sun-3 FPA" (it
 * uses Weitek - not "Wietek" chips, but it's a Sun product), and expanded
 * the extra description to note that it's Weitek-based.
 *
 * Added support for various devices on Auspex systems (which required
 * adding a hack to get around a botch in the "mb_device" table in our
 * current drivers for some of those devices).
 *
 * Separated disks from CD-ROMS, giving each its own probe routine; the
 * disk probe routine will, if a CD-ROM in the drive happens to have a
 * label (as some Sun CD-ROMs do, and as the Auspex system software
 * distribution CD-ROM does), act as if the label is something it should
 * report (it shouldn't; for example, it should report the disk as a
 * CD-ROM, not whatever glop appears in the label).
 *
 * Fixed "ProbeTapeDrive()" to use the no-rewind device, so that it doesn't
 * run the risk of rewinding the tape out from under somebody.
 *
 * Put comments at the front of the probe routines for tapes and CD-ROMs to
 * indicate that they may fail even if the device exists - i.e., if there's
 * no medium in the drive, or if somebody's using the tape drive;
 * unfortunately, I don't know of a good fix for that problem.
 *
 * Revision 1.30  1992/04/26  23:51:53  mcooper
 * Add some comments.
 *
 * Revision 1.28  1992/04/19  23:17:46  mcooper
 * Suppress CodeCenter warning about "romp".
 *
 * Revision 1.27  1992/04/19  23:07:23  mcooper
 * Update GetRomVer() to be more portable.
 *
 * Revision 1.26  1992/04/18  20:48:18  mcooper
 * Add #ifdef HAVE_SUNROMVEC around GetRomVer.
 *
 * Revision 1.25  1992/04/17  23:27:51  mcooper
 * Add support for ROM Version information (Sun only for now).
 *
 * Revision 1.24  1992/04/17  01:07:59  mcooper
 * More de-linting
 *
 * Revision 1.23  1992/04/16  02:25:39  mcooper
 * Bug fixes, de-linting, and other changes found with CodeCenter.
 *
 * Revision 1.22  1992/04/15  02:46:34  mcooper
 * - Add better MainBus() and OPENPROM() build debug messages.
 * - Make GetKernArchName() use "cpu" kernel symbol like GetModelName().
 *
 * Revision 1.21  1992/04/15  02:02:17  mcooper
 * Change GetMemoryStr() to GetMemory().
 *
 * Revision 1.20  1992/04/12  22:03:56  mcooper
 * - Change GetModelName() to use kernel symbol "cpu" instead
 *   of gethostid() to be more portable to other SPARC clones.
 * - Update GetKernArchName() to support Solbourne CPU_TYPE.
 * - Add HAVE_IPI kludge for "id" disks.
 * - Various other cleanup bits.
 *
 * Revision 1.19  1992/03/31  01:55:17  mcooper
 * Use new CheckNlist to check nlist success.
 *
 * Revision 1.18  1992/03/31  00:45:27  mcooper
 * Fixed GetKernArchName() to use CPU_ARCH mask.
 *
 * Revision 1.17  1992/03/31  00:15:09  mcooper
 * Add error check for nlist.n_type.
 *
 * Revision 1.16  1992/03/25  03:28:32  mcooper
 * Skip partitions that have 0 size.
 *
 * Revision 1.15  1992/03/25  03:20:14  mcooper
 * Only read partition info we we're going to print it later.
 *
 * Revision 1.14  1992/03/22  00:20:10  mcooper
 * Major cleanup and re-org.
 *
 * Revision 1.13  1992/03/09  01:23:42  mcooper
 * Add need include files for NIT stuff.
 *
 * Revision 1.12  1992/03/08  04:58:30  mcooper
 * Move probe_generic() to devices.c.
 *
 * Revision 1.11  1992/03/06  18:37:26  mcooper
 * Move some general functions to devices.c.
 *
 * Revision 1.10  1992/03/05  22:36:35  mcooper
 * Cleanup format.
 *
 * Revision 1.9  1992/03/05  05:12:10  mcooper
 * Cleanup build_mainbus().
 *
 * Revision 1.8  1992/03/01  23:30:15  mcooper
 * More more SunOS specific code from sysinfo.c to here.
 *
 * Revision 1.7  1992/02/27  22:01:22  mcooper
 * Add support for getting CPU info for sun4m.
 *
 * Revision 1.6  1992/02/27  20:36:52  mcooper
 * Remove \n from error() messages.
 *
 * Revision 1.5  1992/02/26  19:07:21  mcooper
 * Add a debug statement.
 *
 * Revision 1.4  1992/02/25  00:59:35  mcooper
 * Move tape info to local MTINFO.
 *
 * Revision 1.3  1992/02/25  00:17:45  mcooper
 * Lots of fixes and changes.
 *
 * Revision 1.2  1992/02/22  02:30:29  mcooper
 * Fix fbtab stuff.
 *
 * Revision 1.1  1992/02/22  02:20:19  mcooper
 * Initial revision
 *
 * Revision 1.5  1992/02/19  22:30:44  mcooper
 * Fix calling problem.
 *
 * Revision 1.4  1992/02/17  01:00:23  mcooper
 * - More portability and support for solbourne.
 *
 * Revision 1.3  1992/02/17  00:24:22  mcooper
 * Update frame buffers.
 *
 * Revision 1.2  1992/02/16  22:55:44  mcooper
 * Add netif support.
 *
 * Revision 1.1  1991/11/30  23:28:53  mcooper
 * Initial revision
 *
 */

#include <stdio.h>
#include "system.h"
#include "defs.h"

#include <mntent.h>
#include <nlist.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/buf.h>
#include <sys/stat.h>
#include <sys/mtio.h>
#include <sun/dkio.h>
#include <sun/dklabel.h>
#include <sun/fbio.h>

/*
 * Name of frame buffer "indirect" device.
 */
#define FBDEVICE	"fb"

/*
 * Name of generic magnetic tape device.
 */
#define MTNAME		"mt"

/*
 * Generally used variables
 */
static kvm_t 		       *kd = NULL;
static struct stat 		StatBuf;
static DEVICE 		       *Device;
static char 			Buf[BUFSIZ];
extern char		        CpuSYM[];
extern char		        RomVecSYM[];

static int			OpenPROMTraverse();

#if	defined(HAVE_MAINBUS)
/*
 * Build a device tree by searching the MainBus
 */
#include <sundev/mbvar.h>

#define DV_SIZE	(sizeof(struct mb_device))
#define DR_SIZE (sizeof(struct mb_driver))
extern char		 	MainBusSYM[];

/*
 * Build device tree by looking at mainbus (mb) devices
 */
static int BuildMainBus(TreePtr)
    DEVICE 		       **TreePtr;
{
    static struct nlist		nlistbuf;
    struct nlist	       *nlptr;
    static struct mb_device 	Device;
    static struct mb_driver 	Driver;
    static char 		CtlrName[BUFSIZ], DevName[BUFSIZ];
    static DEVDATA 		DevData;
    u_long 			Addr, DeviceAddr;
    DEVICE 		       *Dev;

    /*
     * Read table address from kernel
     */
    if (!(kd = KVMopen()))
	return(-1);

    if ((nlptr = KVMnlist(kd, MainBusSYM, &nlistbuf)) == NULL)
	return(-1);

    if (CheckNlist(nlptr))
	return(-1);

    /*
     * Read each device table entry.  A NULL device.mb_driver
     * indicates that we're at the end of the table.
     */
    for (DeviceAddr = nlptr->n_value; DeviceAddr; 
	 DeviceAddr += DV_SIZE) {

	/*
	 * Read this device
	 */
	if (KVMread(kd, DeviceAddr, (char *) &Device, DV_SIZE)) {
	    if (Debug) 
		Error("Cannot read mainbus device from address 0x%x.", 
		      DeviceAddr);
	    KVMclose(kd);
	    return(-1);
	}

	/*
	 * See if we're done.
	 */
	if (!Device.md_driver)
	    break;

	/*
	 * Read the driver structure
	 */
	Addr = (u_long) Device.md_driver;
	if (KVMread(kd, Addr, (char *) &Driver, DR_SIZE)) {
	    if (Debug) 
		Error("Cannot read driver for mainbus address 0x%x.", Addr);
	    continue;
	}

	/*
	 * Get the device name
	 */
	if (Addr = (u_long) Driver.mdr_dname) {
	    if (KVMread(kd, Addr, (char *) DevName, sizeof(DevName))) {
		if (Debug)
		    Error("Cannot read device name from address 0x%x.", Addr);
		continue;
	    }
	} else
	    DevName[0] = C_NULL;

	/*
	 * Get the controller name
	 * XXX - not if "Device.md_ctlr" is -1; work around botch
	 * in current Auspex releases, where some boards (File Processor,
	 * Primary Memory, etc.) have both a device and a controller name,
	 * despite the fact that there's not both a controller and a
	 * set of 1 or more devices.
	 */
	if ((Addr = (u_long) Driver.mdr_cname) && Device.md_ctlr != -1) {
	    if (KVMread(kd, Addr, (char *) CtlrName, sizeof(CtlrName))) {
		if (Debug)
		    Error("Cannot read controller name from address 0x%x.", 
			  Addr);
		continue;
	    }
	} else
	    CtlrName[0] = C_NULL;

	/* Make sure devdata is clean */
	bzero(&DevData, sizeof(DEVDATA));

	/* Set what we know */
	if (DevName[0]) {
	    DevData.dd_devname = strdup(DevName);
	    DevData.dd_devunit = Device.md_unit;
	}
	if (CtlrName[0]) {
	    DevData.dd_ctlrname = strdup(CtlrName);
	    DevData.dd_ctlrunit = Device.md_ctlr;
	}
	/* 
	 * Mainbus devices such, as SCSI targets, may not exist
	 * but the controller reports them as present
	 */
	if (Device.md_alive)
	    DevData.dd_flags |= DD_MAYBE_ALIVE;

	if (Debug)
	    printf("MainBus: Found \"%s\" (Unit %d) on \"%s\" (Unit %d) %s\n", 
		   DevData.dd_devname, DevData.dd_devunit,
		   DevData.dd_ctlrname, DevData.dd_ctlrunit,
		   (DevData.dd_flags & DD_MAYBE_ALIVE) ? "[MAYBE-ALIVE]" : "");

	/* Probe and add device */
	if (Dev = ProbeDevice(&DevData, TreePtr))
	    AddDevice(Dev, TreePtr);
    }

    KVMclose(kd);
    return(0);
}
#endif	/* HAVE_MAINBUS */

#if 	defined(HAVE_OPENPROM)
/*
 * OpenPROM stuff
 */
#include <sun/openprom.h>
extern char		 	OpenPROMSYM[];

/*
 * Build device tree by looking at OpenPROM (op)
 */
static int BuildOpenPROM(TreePtr)
    DEVICE 		      **TreePtr;
{
    static struct nlist		nlistbuf;
    struct nlist	       *nlptr;
    static struct dev_info 	Root, *PtrRoot;
    u_long 			Addr;

    if (!(kd = KVMopen()))
	return(-1);

    if ((nlptr = KVMnlist(kd, OpenPROMSYM, &nlistbuf)) == NULL)
	return(-1);

    if (CheckNlist(nlptr))
	return(-1);

    /*
     * Read pointer to "top_devinfo" from kernel
     */
    Addr = nlptr->n_value;
    if (KVMread(kd, Addr, (char *) &PtrRoot, sizeof(struct dev_info *))) {
	if (Debug) Error("Cannot read openprom devinfo pointer from kernel");
	return(-1);
    }

    if (KVMread(kd, (u_long)PtrRoot, (char *)&Root, 
		sizeof(struct dev_info))) {
	if (Debug) Error("Cannot read openprom devinfo root from kernel");
	return(-1);
    }

    return(OpenPROMTraverse(&Root, NULL, TreePtr));
}

/*
 * Check an OpenPROM device.
 */
static int CheckOpenPROMDevice(DevInfo, Parent, TreePtr)
    struct dev_info 		*DevInfo;
    struct dev_info 		*Parent;
    DEVICE 		       **TreePtr;
{
    static DEVDATA 		 DevData;
    DEVICE 			*Device;	

    /* Make sure devdata is clean */
    bzero(&DevData, sizeof(DEVDATA));

    /* Set what we know */
    if (DevInfo && DevInfo->devi_name) {
	DevData.dd_devname = DevInfo->devi_name;
	DevData.dd_devunit = DevInfo->devi_unit;
    }
    if (Parent && Parent->devi_name) {
	DevData.dd_ctlrname = Parent->devi_name;
	DevData.dd_ctlrunit = Parent->devi_unit;
    }
    /* 
     * OpenPROM nodes that have a driver ALWAYS exist.
     * Some nodes may exist, without a driver, however.
     */
    if (DevInfo->devi_driver)
	DevData.dd_flags |= DD_IS_ALIVE;

    if (Debug)
	printf("OPENPROM: Found \"%s\" (Unit %d) on \"%s\" (Unit %d) %s\n", 
	       DevData.dd_devname, DevData.dd_devunit,
	       DevData.dd_ctlrname, DevData.dd_ctlrunit,
	       (DevData.dd_flags & DD_IS_ALIVE) ? "[ALIVE]" : "");

    /* Probe and add device */
    if (Device = (DEVICE *) ProbeDevice(&DevData, TreePtr))
	AddDevice(Device, TreePtr);
}

/*
 * Recursively traverse and descend the OpenPROM devinfo tree.
 */
static int OpenPROMTraverse(DevPtr, Parent, TreePtr)
    struct dev_info 		*DevPtr;
    struct dev_info 		*Parent;
    DEVICE 		       **TreePtr;
{
    static char 		 Name[BUFSIZ];
    struct dev_info 		*Ptr;

    /*
     * If node name is a valid pointer, read the name from kernel space
     * and call openprom_probe to handle checking the device.
     */
    if (DevPtr->devi_name) {
	if (KVMread(kd, (u_long) DevPtr->devi_name, (char *) Name, 
		    sizeof(Name))) {
	    Error("Cannot read openprom device name.");
	    Name[0] = C_NULL;
	} else {
	    DevPtr->devi_name = (char *) strdup(Name);
	    CheckOpenPROMDevice(DevPtr, Parent, TreePtr);
	}
    }

    /*
     * If this node has slaves, read the slave data from kernel space
     * and descend.
     */
    if (DevPtr->devi_slaves) {
	Ptr = (struct dev_info *) xcalloc(1, sizeof(struct dev_info));
	if (KVMread(kd, (u_long) DevPtr->devi_slaves, (char *) Ptr,
		    sizeof(struct dev_info))) {
	    Error("Cannot read openprom slave data for %s.", Name);
	} else {
	    DevPtr->devi_slaves = (struct dev_info *) Ptr;
	    OpenPROMTraverse(DevPtr->devi_slaves, DevPtr, TreePtr);
	}
    }

    /*
     * If this node has a next pointer, read the next data from kernel space
     * and traverse.
     */
    if (DevPtr->devi_next) {
	Ptr = (struct dev_info *) xcalloc(1, sizeof(struct dev_info));
	if (KVMread(kd, (u_long) DevPtr->devi_next, (char *) Ptr,
		    sizeof(struct dev_info))) {
	    Error("Cannot read openprom next data for %s.", Name);
	} else {
	    DevPtr->devi_next = (struct dev_info *) Ptr;
	    OpenPROMTraverse(DevPtr->devi_next, Parent, TreePtr);
	}
    }

    return(0);
}
#endif /* HAVE_OPENPROM */

/*
 * Build device tree using TreePtr.
 * Calls bus and method specific functions to
 * search for devices.
 */
extern int BuildDevicesSunOS(TreePtr)
    DEVICE 		       **TreePtr;
{
    int 			 Found = 1;

#if	defined(HAVE_OPENPROM)
    if (BuildOpenPROM(TreePtr) == 0)
	Found = 0;
#endif	/* HAVE_OPENPROM */

#if	defined(HAVE_MAINBUS)
    if (BuildMainBus(TreePtr) == 0)
	Found = 0;
#endif	/* HAVE_MAINBUS */

    return(Found);
}

/*
 * Scan the Disk Controller table looking for
 * a specific type.
 */
static DKCTLRTAB *GetDkCtlrTab(DkCtrlType)
    int 			DkCtrlType;
{
    extern DKCTLRTAB 		DkCtlrTab[];
    register int 		i;

    for (i = 0; DkCtlrTab[i].ct_model; ++i) {
	if (DkCtrlType == DkCtlrTab[i].ct_ctype)
	    return(&DkCtlrTab[i]);
    }

    return((DKCTLRTAB *) NULL);
}

/*
 * Scan the Frame Buffer table looking for 
 * a specific fb type.
 */
static NAMETAB *GetFBTab(FBType)
    int 			FBType;
{
    extern NAMETAB 		FBTab[];
    register int 		i;

    for (i = 0; FBTab[i].name; ++i) {
	if (FBType == FBTab[i].value)
	    return(&FBTab[i]);
    }

    return((NAMETAB *) NULL);
}

/*
 * Get disk info structure.
 */
static struct dk_info *GETdk_info(d, file)
    int 			d;
    char 		       *file;
{
    static struct dk_info 	dk_info;

    if (ioctl(d, DKIOCINFO, &dk_info) < 0) {
	if (Debug) Error("%s: DKIOCINFO: %s.", file, SYSERR);
	return(NULL);
    }

    return(&dk_info);
}

/*
 * Get disk configuration structure.
 */
static struct dk_conf *GETdk_conf(d, file)
    int 			d;
    char 		       *file;
{
    static struct dk_conf 	dk_conf;

    if (ioctl(d, DKIOCGCONF, &dk_conf) < 0) {
	if (Debug) Error("%s: DKIOCGCONF: %s.", file, SYSERR);
	return(NULL);
    }

    return(&dk_conf);
}

/*
 * Get disk geometry structure.
 */
static struct dk_geom *GETdk_geom(d, file)
    int 			d;
    char 		       *file;
{
    static struct dk_geom 	dk_geom;

    if (ioctl(d, DKIOCGGEOM, &dk_geom) < 0) {
	if (Debug) Error("%s: DKIOCGGEOM: %s.", file, SYSERR);
	return(NULL);
    }

    return(&dk_geom);
}

/*
 * Get disk type structure.
 */
static struct dk_type *GETdk_type(d, file)
    int 			d;
    char 		       *file;
{
    static struct dk_type 	dk_type;

    if (ioctl(d, DKIOCGTYPE, &dk_type) < 0) {
	if (errno != ENOTTY)
	    if (Debug) Error("%s: DKIOCGTYPE: %s.", file, SYSERR);
	return(NULL);
    }

    return(&dk_type);
}

/*
 * Check the checksum of a disklabel.
 */
static int DkLblCheckSum(DkLabel)
    struct dk_label 	       *DkLabel;
{
    register short 	       *Ptr, Sum = 0;
    register short 		Count;

    Count = (sizeof (struct dk_label)) / (sizeof (short));
    Ptr = (short *)DkLabel;

    /*
     * Take the xor of all the half-words in the label.
     */
    while (Count--)
	Sum ^= *Ptr++;

    /*
     * The total should be zero for a correct checksum
     */
    return(Sum);
}

/*
 * Get label information from label on disk.
 * The label is stored in the first sector of the disk.
 * We use the driver specific "read" flag with the DKIOCSCMD
 * ioctl to read the first sector.  There should be a special
 * ioctl to just read the label.
 */
static struct dk_label *GETdk_label(d, file, dk_info)
    int 			d;
    char 		       *file;
    struct dk_info 	       *dk_info;
{
    static struct dk_label 	dk_label;
    struct dk_cmd 		dk_cmd;
    struct dkctlrtab 	       *pct;

    if (!file || !dk_info)
	return((struct dk_label *) NULL);

    if (!(pct = GetDkCtlrTab((int) dk_info->dki_ctype))) {
	Error("Controller type %d is unknown.", 
	      dk_info->dki_ctype);
	return((struct dk_label *) NULL);
    }

    if (pct->ct_rdcmd < 0) {
	if (Debug)
	    Error("Read block on controller type \"%s\" is unsupported.",
		  pct->ct_model);
	return((struct dk_label *) NULL);
    }

    bzero((char *) &dk_cmd, sizeof(dk_cmd));
    dk_cmd.dkc_cmd = pct->ct_rdcmd;
    dk_cmd.dkc_flags = DK_SILENT | DK_ISOLATE;
    dk_cmd.dkc_blkno = (daddr_t)0;
    dk_cmd.dkc_secnt = 1;
    dk_cmd.dkc_bufaddr = (char *) &dk_label;
    dk_cmd.dkc_buflen = SECSIZE;

    if (ioctl(d, DKIOCSCMD, &dk_cmd) < 0) {
	if (Debug) Error("%s: DKIOCSCMD: %s.", file, SYSERR);
	return((struct dk_label *) NULL);
    }

    if (dk_label.dkl_magic != DKL_MAGIC) {
	Error("%s: Disk not labeled.", file);
	return((struct dk_label *) NULL);
    }

    if (DkLblCheckSum(&dk_label)) {
	Error("%s: Bad label checksum.", file);
	return((struct dk_label *) NULL);
    }

    return(&dk_label);
}

/*
 * Get the name of a disk (i.e. sd0).
 */
static char *GetDiskName(name, dk_conf, dk_info)
    char 		       *name;
    struct dk_conf 	       *dk_conf;
    struct dk_info 	       *dk_info;
{
    if (!dk_conf || !dk_info) {
	if (name)
	    return(name);
	return((char *) NULL);
    }

#if	defined(DKI_HEXUNIT)
    if (FLAGS_ON(dk_info->dki_flags, DKI_HEXUNIT))
	(void) sprintf(Buf, "%s%3.3x", dk_conf->dkc_dname, dk_conf->dkc_unit);
    else
#endif 	/* DKI_HEXUNIT */
	(void) sprintf(Buf, "%s%d", dk_conf->dkc_dname, dk_conf->dkc_unit);

    return(strdup(Buf));
}

/*
 * Get the name of the controller for a disk.
 */
static char *GetDkCtlrName(dk_conf)
    struct dk_conf 	       *dk_conf;
{
    if (!dk_conf)
	return((char *) NULL);

    (void) sprintf(Buf, "%s%d", dk_conf->dkc_cname, dk_conf->dkc_cnum);

    return(strdup(Buf));
}

/*
 * Get the disk controller model name from a disk.
 */
static char *GetDkCtlrModel(dk_info)
    struct dk_info 	       *dk_info;
{
    struct dkctlrtab 	       *pct;

    if (!dk_info)
	return((char *) NULL);

    if (!(pct = GetDkCtlrTab(dk_info->dki_ctype)))
	return(NULL);

    return(pct->ct_model);
}

/*
 * Get a disk controller device from disk info.
 */
static DEVICE *GetDkCtlrDevice(DevData, dk_info, dk_conf)
    DEVDATA 		       *DevData;
    struct dk_info 	       *dk_info;
    struct dk_conf 	       *dk_conf;
{
    DEVICE 		       *MkMasterFromDevData();
    DEVICE 		       *dkctlr;

    if ((dkctlr = NewDevice(NULL)) == NULL)
	return((DEVICE *) NULL);

    bzero((char *) dkctlr, sizeof(*dkctlr));

    dkctlr->dv_type = DT_DISKCTLR;

    /*
     * Get name of controller from devdata if available
     */
    if (DevData && DevData->dd_ctlrname) {
	dkctlr = MkMasterFromDevData(DevData);
    }

    if (dk_conf) {
	if (!dkctlr->dv_name) {
	    dkctlr->dv_name = GetDkCtlrName(dk_conf);
	    dkctlr->dv_unit = dk_conf->dkc_cnum;
	}
	dkctlr->dv_addr = dk_conf->dkc_addr;
	dkctlr->dv_prio = dk_conf->dkc_prio;
	dkctlr->dv_vec = dk_conf->dkc_vec;
    }

    if (dk_info) {
	dkctlr->dv_model = GetDkCtlrModel(dk_info);
    }

    return(dkctlr);
}

/*
 * Get disk label info from the extracted dk_label info.
 */
static char *GetDiskLabel(dk_label)
    struct dk_label 	       *dk_label;
{
    register char 	       *p;

    if (!dk_label)
	return((char *) NULL);

    (void) strcpy(Buf, dk_label->dkl_asciilabel);

    /*
     * The label normally has geometry information in it we don't want
     * to see, so we trim out anything starting with " cyl".
     */
    for (p = Buf; p && *p; ++p) {
	if (*p == ' ' && strncasecmp(p, " cyl", 4) == 0)
	    *p = C_NULL;
    }

    return(strdup(Buf));
}

/*
 * Get filesystem mount info for a partition.
 */
static char *GetMountInfo(name, part)
    char 		       *name;
    char 		       *part;
{
    FILE 		       *mf;
    struct mntent 	       *mntent;
    char 		       *file;

    if (!name)
	return((char *) NULL);

    file = GetCharFile(name, part);

    if ((mf = setmntent(MNTTAB, "r")) == NULL) {
	Error("%s: Cannot open for reading: %s.", MNTTAB, SYSERR);
	return(NULL);
    }

    while (mntent = getmntent(mf)) {
	if (strcmp(mntent->mnt_fsname, file) == 0)
	    break;
    }

    endmntent(mf);

    return((mntent) ? mntent->mnt_dir : (char *) NULL);
}

/*
 * Extract the disk partition info from a disk.
 */
static DISKPART *ExtractDiskPart(name, part, dk_conf, dk_geom)
    char 		       *name;
    char 		       *part;
    struct dk_conf 	       *dk_conf;
    struct dk_geom 	       *dk_geom;
{
    static DISKPART 		diskpart;
    struct dk_map 		dk_map;
    char 		       *file;
    char 		       *p;
    int 			d;

    if (!name || !dk_conf || !dk_geom)
	return((DISKPART *) NULL);

    file = GetRawFile(name, part);

    if (stat(file, &StatBuf) != 0) {
	if (Debug) Error("%s: No such partition.", file);
	return((DISKPART *) NULL);
    }

    if ((d = open(file, O_RDONLY)) < 0) {
	if (Debug)
	    Error("%s: Cannot open for read: %s.", file, SYSERR);
	return((DISKPART *) NULL);
    }

    if (ioctl(d, DKIOCGPART, &dk_map) != 0) {
	Error("%s: Cannot extract partition info: %s.", 
		file, SYSERR);
	return((DISKPART *) NULL);
    }
 
    (void) close(d);

    /*
     * Skip empty partitions
     */
    if (!dk_map.dkl_nblk) {
	if (Debug) Error("%s: partition has no size.", file);
	return((DISKPART *) NULL);
    }

    bzero((char *) &diskpart, sizeof(DISKPART));

    diskpart.dp_name = strdup(part);

    if (p = GetMountInfo(name, part))
	diskpart.dp_mnt = strdup(p);
    /* 
     * If this is the "b" partition on the root device, 
     *  then assume it's swap 
     */
    else if (dk_conf->dkc_unit == 0 && strcmp(part, "b") == 0)
	diskpart.dp_mnt = "swap";

    diskpart.dp_stsect = dk_map.dkl_cylno *
	(dk_geom->dkg_nhead * dk_geom->dkg_nsect);
    diskpart.dp_nsect = dk_map.dkl_nblk;

    return(&diskpart);
}

/*
 * Translate disk partition information from basic
 * extracted disk info.
 */
static DISKPART *GetDiskPart(name, dk_conf, dk_geom)
    char 		       *name;
    struct dk_conf 	       *dk_conf;
    struct dk_geom 	       *dk_geom;
{
    extern char 		PartChars[];
    register DISKPART 	       *pdp, *dp;
    register int 		i;
    static char 		pname[2];
    DISKPART 		       *base = NULL;

    if (!name || !dk_conf || !dk_geom)
	return((DISKPART *) NULL);

    pname[1] = C_NULL;
    for (i = 0; PartChars[i]; ++i) {
	pname[0] = PartChars[i];
	if (dp = ExtractDiskPart(name, pname, dk_conf, dk_geom)) {
	    if (base) {
		for (pdp = base; pdp && pdp->dp_nxt; pdp = pdp->dp_nxt);
		pdp->dp_nxt = NewDiskPart(dp);
	    } else {
		base = NewDiskPart(dp);
	    }
	}
    }

    return(base);
}

/*
 * Convert all we've learned about a disk to a DEVICE.
 */
static DEVICE *dkToDiskDevice(name, DevData,
			      dk_info, dk_label, dk_conf, dk_geom, dk_type)
    char 		       *name;
    DEVDATA 		       *DevData;
    struct dk_info 	       *dk_info;
    struct dk_label 	       *dk_label;
    struct dk_conf 	       *dk_conf;
    struct dk_geom 	       *dk_geom;
    struct dk_type 	       *dk_type;
{
    DEVICE 		       *Device, *dkctlr;
    DISKDRIVE 		       *diskdrive;

    if ((Device = NewDevice(NULL)) == NULL) {
	Error("Cannot create new device entry.");
	return((DEVICE *) NULL);
    }

    if ((dkctlr = NewDevice(NULL)) == NULL) {
	Error("Cannot create new dkctlr device entry.");
	return((DEVICE *) NULL);
    }

    if ((diskdrive = NewDiskDrive(NULL)) == NULL) {
	Error("Cannot create new diskdrive entry.");
	return((DEVICE *) NULL);
    }

    Device->dv_name = GetDiskName(name, dk_conf, dk_info);
    Device->dv_type = DT_DISKDRIVE;
    /*
     * Only read partition info we we're going to print it later.
     */
    if (VL_ALL)
	diskdrive->dd_part = GetDiskPart(name, dk_conf, dk_geom);
    diskdrive->dd_label = GetDiskLabel(dk_label);
    Device->dv_model = diskdrive->dd_label;

    if (dk_conf) {
	diskdrive->dd_unit = dk_conf->dkc_unit;
	diskdrive->dd_slave = dk_conf->dkc_slave;;
    }
    if (dk_geom) {
	diskdrive->dd_dcyl = dk_geom->dkg_ncyl;
	diskdrive->dd_pcyl = dk_geom->dkg_pcyl;
	diskdrive->dd_acyl = dk_geom->dkg_acyl;
	diskdrive->dd_heads = dk_geom->dkg_nhead;
	diskdrive->dd_sect = dk_geom->dkg_nsect;
	diskdrive->dd_apc = dk_geom->dkg_apc;
	diskdrive->dd_rpm = dk_geom->dkg_rpm;
	diskdrive->dd_intrlv = dk_geom->dkg_intrlv;
    }
    if (dk_type) {
	diskdrive->dd_psect = dk_type->dkt_hsect;
	diskdrive->dd_promrev = dk_type->dkt_promrev;
    }
    if (dk_info) {
#if	defined(DKI_HEXUNIT)
	if (FLAGS_ON(dk_info->dki_flags, DKI_HEXUNIT))
	    diskdrive->dd_flags |= DF_HEXUNIT;
#endif 	/* DKI_HEXUNIT */
    }

    diskdrive->dd_secsize = SECSIZE;
    if (diskdrive->dd_dcyl && diskdrive->dd_sect && diskdrive->dd_heads) {
	static char Buf[BUFSIZ];

	diskdrive->dd_size = nsect_to_bytes(diskdrive->dd_dcyl * 
					     diskdrive->dd_sect * 
					     diskdrive->dd_heads, 
					    diskdrive->dd_secsize);

	(void) sprintf(Buf, "%.2f MB capacity", 
		       (float) bytes_to_mbytes(diskdrive->dd_size));
	Device->dv_desc = strdup(Buf);
    }

    dkctlr = GetDkCtlrDevice(DevData, dk_info, dk_conf);

    Device->dv_devspec = (caddr_t *) diskdrive;
    Device->dv_master = dkctlr;

    return(Device);
}

/*
 * Convert all we've learned about a CD-ROM drive to a DEVICE.
 */
static DEVICE *dkToCDROMDevice(name, DevData,
			      dk_info, dk_conf, dk_geom, dk_type)
    char 		       *name;
    DEVDATA 		       *DevData;
    struct dk_info 	       *dk_info;
    struct dk_conf 	       *dk_conf;
    struct dk_geom 	       *dk_geom;
    struct dk_type 	       *dk_type;
{
    DEVICE 		       *Device, *dkctlr;
    DISKDRIVE 		       *diskdrive;

    if ((Device = NewDevice(NULL)) == NULL) {
	Error("Cannot create new device entry.");
	return((DEVICE *) NULL);
    }

    if ((dkctlr = NewDevice(NULL)) == NULL) {
	Error("Cannot create new dkctlr device entry.");
	return((DEVICE *) NULL);
    }

    if ((diskdrive = NewDiskDrive(NULL)) == NULL) {
	Error("Cannot create new diskdrive entry.");
	return((DEVICE *) NULL);
    }

    Device->dv_name = GetDiskName(name, dk_conf, dk_info);
    Device->dv_type = DT_DISKDRIVE;
    /*
     * Only read partition info we we're going to print it later.
     */
    if (VL_ALL)
	diskdrive->dd_part = GetDiskPart(name, dk_conf, dk_geom);
    diskdrive->dd_label = NULL;
    Device->dv_model = "CD-ROM";

    if (dk_conf) {
	diskdrive->dd_unit = dk_conf->dkc_unit;
	diskdrive->dd_slave = dk_conf->dkc_slave;;
    }
    if (dk_geom) {
	diskdrive->dd_dcyl = dk_geom->dkg_ncyl;
	diskdrive->dd_pcyl = dk_geom->dkg_pcyl;
	diskdrive->dd_acyl = dk_geom->dkg_acyl;
	diskdrive->dd_heads = dk_geom->dkg_nhead;
	diskdrive->dd_sect = dk_geom->dkg_nsect;
	diskdrive->dd_apc = dk_geom->dkg_apc;
	diskdrive->dd_rpm = dk_geom->dkg_rpm;
	diskdrive->dd_intrlv = dk_geom->dkg_intrlv;
    }
    if (dk_type) {
	diskdrive->dd_psect = dk_type->dkt_hsect;
	diskdrive->dd_promrev = dk_type->dkt_promrev;
    }

    diskdrive->dd_secsize = SECSIZE;
    if (diskdrive->dd_dcyl && diskdrive->dd_sect && diskdrive->dd_heads) {
	static char Buf[BUFSIZ];

	diskdrive->dd_size = nsect_to_bytes(diskdrive->dd_dcyl * 
					     diskdrive->dd_sect * 
					     diskdrive->dd_heads, 
					    diskdrive->dd_secsize);

	(void) sprintf(Buf, "%.2f MB capacity", 
		       (float) bytes_to_mbytes(diskdrive->dd_size));
	Device->dv_desc = strdup(Buf);
    }

    dkctlr = GetDkCtlrDevice(DevData, dk_info, dk_conf);

    Device->dv_devspec = (caddr_t *) diskdrive;
    Device->dv_master = dkctlr;

    return(Device);
}

/*
 * Query and learn about a disk.
 */
extern DEVICE *ProbeDiskDrive(name, DevData, DevDataTab)
     /*ARGSUSED*/
    char 		       *name;
    DEVDATA 		       *DevData;
    DEVDATATAB 	     	       *DevDataTab;
{
    DEVICE 		       *diskdevice;
    struct dk_info 	       *dk_info = NULL;
    struct dk_conf 	       *dk_conf = NULL;
    struct dk_type 	       *dk_type = NULL;
    struct dk_label 	       *dk_label = NULL;
    struct dk_geom 	       *dk_geom = NULL;
    char 		       *rfile;
    int 			d;

    if (!name)
	return((DEVICE *) NULL);

#if	defined(HAVE_IPI)
    /*
     * XXX - Kludge for IPI "id" disks.
     */
    if (EQ(DevData->dd_devname, "id")) {
	static char		Buf[BUFSIZ];

	(void) sprintf(Buf, "%s%3.3x", 
		       DevData->dd_devname, DevData->dd_devunit);
	name = Buf;
    }
#endif	/* HAVE_IPI */

    if (stat(rfile = GetRawFile(name, NULL), &StatBuf) != 0) {
	/*
	 * Get the name of the whole disk raw device.
	 */
	rfile = GetRawFile(name, "c");
    }

    if ((d = open(rfile, O_RDONLY)) < 0) {
	if (Debug) Error("%s: Cannot open for reading: %s.", rfile, SYSERR);
	/*
	 * If we know for sure this drive is present and we
	 * know something about it, then create a minimal device.
	 */
	if ((DevDataTab->ddt_model || DevDataTab->ddt_desc) &&
	    FLAGS_ON(DevData->dd_flags, DD_IS_ALIVE)) {
	    Device = NewDevice((DEVICE *) NULL);
	    Device->dv_name = strdup(name);
	    Device->dv_unit = DevData->dd_devunit;
	    Device->dv_master = MkMasterFromDevData(DevData);
	    Device->dv_type = DT_DISKDRIVE;
	    Device->dv_model = DevDataTab->ddt_model;
	    Device->dv_desc = DevDataTab->ddt_desc;
	    return(Device);
	} else
	    return((DEVICE *) NULL);
    }

    if ((dk_conf = GETdk_conf(d, rfile)) == NULL) {
	if (Debug) Error("%s: get dk_conf failed.", rfile);
    }

    if ((dk_info = GETdk_info(d, rfile)) == NULL) {
	if (Debug) Error("%s: get dk_info failed.", rfile);
    }

    if ((dk_geom = GETdk_geom(d, rfile)) == NULL) {
	if (Debug) Error("%s: get dk_geom failed.", rfile);
    }

    if ((dk_label = GETdk_label(d, rfile, dk_info)) == NULL) {
	if (Debug) Error("%s: get dk_label failed.", rfile);
    }

    /*
     * Not all controllers support dk_type
     */
    dk_type = GETdk_type(d, rfile);

    close(d);

    if (!(diskdevice = dkToDiskDevice(name, DevData,
					dk_info, dk_label, 
					dk_conf, dk_geom, dk_type))) {
	Error("%s: Cannot convert diskdrive information.", name);
	return((DEVICE *) NULL);
    }

    return(diskdevice);
}

/*
 * Probe a tape device
 * XXX - this loses if somebody's using the tape, as tapes are exclusive-open
 * devices, and our open will therefore fail.
 * This also loses if there's no tape in the drive, as the open will fail.
 * The above probably applies to most other flavors of UNIX.
 */
extern DEVICE *ProbeTapeDrive(name, DevData, DevDataTab)
     /*ARGSUSED*/
    char 		       *name;
    DEVDATA 		       *DevData;
    DEVDATATAB	 	       *DevDataTab;
{
    extern NAMETAB		MtInfo[];
    DEVICE 		       *Device;
    char 		       *file;
    char 		       *model = NULL;
    char			rfile[BUFSIZ];
    static char 		Buf[BUFSIZ];
    struct mtget 		mtget;
    register int 		i;
    int 			d;

    /*
     * Don't use GetRawFile; that'll just stick an "r" in front of the
     * device name, meaning it'll return the rewind-on-close device.
     * Somebody may have left the tape positioned somewhere other than
     * at the BOT to, for example, write a dump there later in the
     * evening; it'd be rather rude to reposition it out from under them.
     *
     * The above probably applies to most other flavors of UNIX.
     */
    if (!name)
	file = NULL;
    else {
	(void) sprintf(rfile, "/dev/nr%s", name);
	file = rfile;
    }

    if ((d = open(file, O_RDONLY)) < 0) {
	if (Debug)
	    Error("%s Cannot open for read: %s.", file, SYSERR);

	/*
	 * --RECURSE--
	 * If we haven't tried the "mt" name yet, try it now
	 */
	if (strncmp(name, MTNAME, strlen(MTNAME)) != 0) {
	    (void) sprintf(Buf, "%s%d", MTNAME, DevData->dd_devunit);
	    Device = ProbeTapeDrive(Buf, DevData, DevDataTab);
	    if (Device)
		return(Device);
	}

	/*
	 * If we know for sure this drive is present and we
	 * know something about it, then create a minimal device.
	 */
	if ((DevDataTab->ddt_model || DevDataTab->ddt_desc) &&
	    FLAGS_ON(DevData->dd_flags, DD_IS_ALIVE)) {
	    Device = NewDevice((DEVICE *) NULL);
	    /* 
	     * Recreate name from devdata since we might have had to
	     * call ourself with name "rmt?"
	     */
	    (void) sprintf(Buf, "%s%d", DevData->dd_devname, 
			   DevData->dd_devunit);
	    Device->dv_name = strdup(Buf);
	    Device->dv_unit = DevData->dd_devunit;
	    Device->dv_master = MkMasterFromDevData(DevData);
	    Device->dv_type = DT_TAPEDRIVE;
	    Device->dv_model = DevDataTab->ddt_model;
	    Device->dv_desc = DevDataTab->ddt_desc;
	    return(Device);
	} else
	    return((DEVICE *) NULL);
    }

    if (ioctl(d, MTIOCGET, &mtget) != 0) {
	Error("%s: Cannot extract tape status: %s.", file, SYSERR);
	return((DEVICE *) NULL);
    }

    (void) close(d);

    model = "unknown";

    for (i = 0; MtInfo[i].name; ++i) {
	if ((MtInfo[i].value == mtget.mt_type)) {
	    model = MtInfo[i].name;
	    break;
	}
    }

    /*
     * Create and set device info
     */
    Device = NewDevice(NULL);
    Device->dv_name = strdup(name);
    Device->dv_type = DT_TAPEDRIVE;
    if (model)
	Device->dv_model = model;
    else
	Device->dv_model = DevDataTab->ddt_model;
    Device->dv_desc = DevDataTab->ddt_desc;
    Device->dv_unit = DevData->dd_devunit;
    Device->dv_master = MkMasterFromDevData(DevData);

    return(Device);
}

/*
 * Query and learn about a CD-ROM drive.
 * We don't just use ProbeDisk Drive so that we don't get confused if
 * the CD-ROM in the drive has a label.
 * XXX - this loses if there's no caddy in the drive, as the open will fail.
 */
extern DEVICE *ProbeCDROMDrive(name, DevData, DevDataTab)
     /*ARGSUSED*/
    char 		       *name;
    DEVDATA 		       *DevData;
    DEVDATATAB 	     	       *DevDataTab;
{
    DEVICE 		       *diskdevice;
    struct dk_info 	       *dk_info = NULL;
    struct dk_conf 	       *dk_conf = NULL;
    struct dk_type 	       *dk_type = NULL;
    struct dk_geom 	       *dk_geom = NULL;
    char 		       *rfile;
    int 			d;

    if (!name)
	return((DEVICE *) NULL);

    rfile = GetRawFile(name, NULL);

    if ((d = open(rfile, O_RDONLY)) < 0) {
	if (Debug) Error("%s: Cannot open for reading: %s.", rfile, SYSERR);
	/*
	 * If we know for sure this drive is present and we
	 * know something about it, then create a minimal device.
	 */
	if ((DevDataTab->ddt_model || DevDataTab->ddt_desc) &&
	    FLAGS_ON(DevData->dd_flags, DD_IS_ALIVE)) {
	    Device = NewDevice((DEVICE *) NULL);
	    Device->dv_name = strdup(name);
	    Device->dv_unit = DevData->dd_devunit;
	    Device->dv_master = MkMasterFromDevData(DevData);
	    Device->dv_type = DT_DISKDRIVE;
	    Device->dv_model = DevDataTab->ddt_model;
	    Device->dv_desc = DevDataTab->ddt_desc;
	    return(Device);
	} else
	    return((DEVICE *) NULL);
    }

    if ((dk_conf = GETdk_conf(d, rfile)) == NULL) {
	if (Debug) Error("%s: get dk_conf failed.", rfile);
    }

    if ((dk_info = GETdk_info(d, rfile)) == NULL) {
	if (Debug) Error("%s: get dk_info failed.", rfile);
    }

    if ((dk_geom = GETdk_geom(d, rfile)) == NULL) {
	if (Debug) Error("%s: get dk_geom failed.", rfile);
    }

    /*
     * Not all controllers support dk_type
     */
    dk_type = GETdk_type(d, rfile);

    close(d);

    if (!(diskdevice = dkToCDROMDevice(name, DevData,
					dk_info, dk_conf, dk_geom,
					dk_type))) {
	Error("%s: Cannot convert CD-ROM drive information.", name);
	return((DEVICE *) NULL);
    }

    return(diskdevice);
}

/*
 * Query and learn about a device attached to an Auspex Storage Processor.
 * They'll show up as "ad" in the Mainbus info structure, but that
 * merely reflects the way the slots are set up in the config file.
 * We need to find out what type of device it is at this particular
 * instant (it's subject to change - perhaps even while we're running,
 * but there's not a heck of a lot we can do about that).
 *
 * We do that by trying it as a CD-ROM first, then as a disk, then as
 * a tape; that loses if it's a tape and somebody's using it, but
 * tapes on most if not all UNIX systems can't be probed by us if
 * somebody's using it.
 * The reason why we try it first as a CD-ROM is that if the CD has a
 * partition table, the Auspex driver lets you open the partitions as
 * if it were a disk.
 */
extern DEVICE *ProbeSPDrive(name, DevData, DevDataTab)
     /*ARGSUSED*/
    char 		       *name;
    DEVDATA 		       *DevData;
    DEVDATATAB 	     	       *DevDataTab;
{
    DEVICE 		       *thedevice;
    char			devname[BUFSIZ];

    /*
     * Try it first as a CD-ROM.
     */
    (void) sprintf(devname, "acd%d", DevData->dd_devunit);
    DevData->dd_devname = "acd";
    DevDataTab->ddt_model = "CD-ROM";
    if (thedevice = ProbeCDROMDrive(devname, DevData, DevDataTab))
	return(thedevice);

    /*
     * Not a CD-ROM.  Try a disk.
     */
    (void) sprintf(devname, "ad%d", DevData->dd_devunit);
    DevData->dd_devname = "ad";
    DevDataTab->ddt_model = NULL;
    if (thedevice = ProbeDiskDrive(devname, DevData, DevDataTab))
	return(thedevice);

    /*
     * Not a disk.  Try a tape.
     */
    (void) sprintf(devname, "ast%d", DevData->dd_devunit);
    DevData->dd_devname = "ast";
    if (thedevice = ProbeTapeDrive(devname, DevData, DevDataTab))
	return(thedevice);

    /*
     * None of the above.  Who knows?
     */
    return((DEVICE *) NULL);
}

/*
 * Probe a CPU.  
 *
 * This function really "fakes" up an entry.
 *
 * XXX Currently the info only comes from the OpenPROM.  This should
 * be converted to use the kernel mach_info and mod_info structures.
 */
extern DEVICE *ProbeCPU(name, DevData, DevDataTab)
     /*ARGSUSED*/
    char 		       *name;
    DEVDATA 		       *DevData;
    DEVDATATAB	 	       *DevDataTab;
{
    static int 			num_cpus = 0;

    Device = NewDevice(NULL);
    (void) sprintf(Buf, "cpu%d", num_cpus++);
    Device->dv_name = strdup(Buf);
    Device->dv_type = DT_CPU;
    Device->dv_model = strdup(name);
    Device->dv_desc = DevDataTab->ddt_desc;
    Device->dv_master = MkMasterFromDevData(DevData);

    return(Device);
}

/*
 * Probe a FrameBuffer.
 */
extern DEVICE *ProbeFrameBuffer(name, DevData, DevDataTab)
    char 		       *name;
    DEVDATA 		       *DevData;
    DEVDATATAB	 	       *DevDataTab;
{
    DEVICE 		       *fbdevice;
    FRAMEBUFFER 	       *fb;
    NAMETAB 		       *fbtab;
    struct fbgattr 		fbattr;
#ifdef FBIOGXINFO
    struct cg6_info 		cg6_info;
#endif
    char 		       *file, Buf[BUFSIZ];
    int 			d;

    if (!name)
	return((DEVICE *) NULL);

    /*
     * Check the device file.  If the stat fails because
     * the device doesn't exist, trying the default framebuffer
     * device /dev/fb.
     */
    file = GetCharFile(name, NULL);
    if (stat(file, &StatBuf) != 0) {
	if (errno == ENOENT && !EQ(name, FBDEVICE)) {
	    if (Debug) 
		Error("Framebuffer device %s does not exist.  Trying `fb'.",
		      name);
	    return(ProbeFrameBuffer(FBDEVICE, DevData, DevDataTab));
	}
    }

    if ((d = open(file, O_RDONLY)) < 0) {
	if (Debug) Error("%s: Cannot open for reading: %s.", file, SYSERR);
	return((DEVICE *) NULL);
    }

    if (ioctl(d, FBIOGATTR, &fbattr) != 0) {
	if (ioctl(d, FBIOGTYPE, &fbattr.fbtype) != 0) {
	    if (Debug) Error("%s: FBIOGATTR/FBIOGTYPE: %s.", 
			     file, SYSERR);
	    return((DEVICE *) NULL);
	}
    }

    Buf[0] = C_NULL;
#if	defined(FBIOGXINFO)
    if (ioctl(d, FBIOGXINFO, &cg6_info) == 0) {
	sprintf(Buf, "SBus Slot %d, Revision %d",
		cg6_info.slot, cg6_info.boardrev);
	if (cg6_info.hdb_capable)
	    (void) strcat(Buf, ", double buffered");
	else
	    (void) strcat(Buf, ", single buffered");
    } else {
	bzero((char *) &cg6_info, sizeof(struct cg6_info));
	if (Debug) Error("%s: FBIOGXINFO: %s.", file, SYSERR);
    }
#endif 	/* FBIOGXINFO */

    close(d);

    if (!(fb = NewFrameBuffer(NULL))) {
	Error("Cannot create new frame buffer.");
	return((DEVICE *) NULL);
    }

    if (!(fbdevice = NewDevice(NULL))) {
	Error("Cannot create new frame buffer device entry.");
	return((DEVICE *) NULL);
    }

    if (!(fbtab = GetFBTab(fbattr.fbtype.fb_type))) {
	Error("Device %s is an unknown type (%d) of frame buffer.",
	      name, fbattr.fbtype.fb_type);
    }

    fbdevice->dv_name = name;
    fbdevice->dv_type = DT_FRAMEBUFFER;
    fbdevice->dv_devspec = (caddr_t *) fb;

    if (Buf[0])
	fbdevice->dv_desc = strdup(Buf);

    if (fbtab) {
	fbdevice->dv_model = fbtab->name;
    } else {
	fbdevice->dv_model = "UNKNOWN";
    }

    fb->fb_height = fbattr.fbtype.fb_height;
    fb->fb_width = fbattr.fbtype.fb_width;
    fb->fb_depth = fbattr.fbtype.fb_depth;
    fb->fb_size = fbattr.fbtype.fb_size;
    fb->fb_cmsize = fbattr.fbtype.fb_cmsize;
#if	defined(FBIOGXINFO)
    if (cg6_info.vmsize)
	fb->fb_vmsize = mbytes_to_bytes(cg6_info.vmsize);
#endif /* FBIOGXINFO */

    fbdevice->dv_master = MkMasterFromDevData(DevData);

    return(fbdevice);
}

/*
 * Determine our cpu model name.
 *
 * We lookup the kernel symbol "cpu" instead of using gethostid()
 * because some SPARC vendors do not encode cpu/model info in gethostid().
 */
extern char *GetModelName()
{
    extern NAMETAB 		ModelTab[];
    struct nlist	       *nlptr;
    register int 		i;
    int				Cpu;
    kvm_t		       *kd;

    if (!(kd = KVMopen()))
	return((char *) NULL);

    if ((nlptr = KVMnlist(kd, CpuSYM, (struct nlist *)NULL)) == NULL)
	return((char *) NULL);

    if (CheckNlist(nlptr))
	return((char *) NULL);

    if (KVMread(kd, (u_long) nlptr->n_value, (char *) &Cpu, sizeof(Cpu))) {
	if (Debug) Error("Cannot read cpu from kernel.");
	return((char *) NULL);
    }

    KVMclose(kd);

    for (i = 0; ModelTab[i].name; ++i)
	if (Cpu == ModelTab[i].value)
	    return(ModelTab[i].name);

    if (Debug)
	Error("No model found; CPU = 0x%x.", Cpu);

    return((char *) NULL);
}

#if 	defined(CPU_ARCH) /* Sun */
#define	ARCH_MASK CPU_ARCH
#endif	/* CPU_ARCH */
#if 	defined(CPU_TYPE) /* Solbourne */
#define ARCH_MASK CPU_TYPE
#endif	/* CPU_TYPE */
/*
 * Determine our kernel architecture name from our hostid.
 */
extern char *GetKernArchName()
{
#if	defined(ARCH_MASK)
    extern NAMETAB 		KernArchTab[];
    struct nlist	       *nlptr;
    kvm_t		       *kd;
    int				Cpu;
    register int 		i;

    if (!(kd = KVMopen()))
	return((char *) NULL);

    if ((nlptr = KVMnlist(kd, CpuSYM, (struct nlist *)NULL)) == NULL)
	return((char *) NULL);

    if (CheckNlist(nlptr))
	return((char *) NULL);

    if (KVMread(kd, (u_long) nlptr->n_value, (char *) &Cpu, sizeof(Cpu))) {
	if (Debug) Error("Cannot read cpu from kernel.");
	return((char *) NULL);
    }

    KVMclose(kd);

    for (i = 0; KernArchTab[i].name; ++i)
	if ((Cpu & ARCH_MASK) == KernArchTab[i].value)
	    return(KernArchTab[i].name);

    if (Debug)
	Error("Kernel Arch 0x%x not defined; Cpu = 0x%x Mask = 0x%x", 
	      Cpu & ARCH_MASK, Cpu, ARCH_MASK);
#endif	/* ARCH_MASK */

    return((char *) NULL);
}

#if	defined(HAVE_NIT)

#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/nit_if.h>

/*
 * Find and set the MAC info using the Network Interface Tap (NIT)
 */
extern void SetMacInfoNIT(DevName, NetIf)
    char 		       *DevName;
    NETIF 		       *NetIf;
{
    register struct sockaddr   *SockAddr;
    struct ifreq 	        ifreq;
    char 		       *ether_ntoa(), Buf[MAXHOSTNAMLEN+1];
    int 		        Desc;

    if (!NetIf)
	return;

    if ((Desc = open("/dev/nit", O_RDONLY)) == SYSFAIL) {
	if (Debug) Error("open /dev/nit failed");
	return;
    }

    /*
     * Bind to NIT for DevName
     */
    strncpy(ifreq.ifr_name, DevName, sizeof ifreq.ifr_name);
    if (ioctl(Desc, NIOCBIND, (caddr_t) &ifreq) < 0) {
	if (Debug) Error("ioctl:  NIOCBIND");
	return;
    }

    /*
     * Get address
     */
    if (ioctl(Desc, SIOCGIFADDR, (caddr_t)&ifreq) < 0) {
	if (Debug) Error("ioctl (SIOCGIFADDR)");
	return;
    }

    (void) close(Desc);

    SockAddr = (struct sockaddr *)&ifreq.ifr_addr;
    NetIf->ni_macaddr = strdup(ether_ntoa((struct ether_addr *) 
					  SockAddr->sa_data));

    if (ether_ntohost(Buf, (struct ether_addr *) SockAddr->sa_data) == 0)
	NetIf->ni_macname = strdup(Buf);
}
#endif	/* HAVE_NIT */

/*
 * Get kernel version string from kernel symbol "version".
 */
extern char *GetKernelVersionStr()
{
    return(GetKernelVersionFromVersion());
}

/*
 * Get amount of physical memory using kernel symbol "physmem".
 */
extern char *GetMemory()
{
    return(GetMemoryFromPhysmem());
}

/*
 * Get system serial number
 */
extern char *GetSerialNoStr()
{
    /* No support */
    return((char *) NULL);
}

/*
 * Get name of OS
 */
extern char *GetOSNameStr()
{
    return(GetOSNameFromUname());
}

/*
 * Get version of OS
 */
extern char *GetOSVersionStr()
{
    return(GetOSVersionFromUname());
}

#if	defined(HAVE_SUNROMVEC)
/*
 * Be backwards compatible with pre-4.1.2 code
 */
#include <mon/sunromvec.h>
#if	defined(OPENPROMS) && !(defined(ROMVEC_VERSION) && \
				(ROMVEC_VERSION == 0 || ROMVEC_VERSION == 1))
#define v_mon_id op_mon_id
#endif
#endif	/* HAVE_SUNROMVEC */

/*
 * Get ROM Version number
 *
 * If "romp" is "defined" (in <mon/sunromvec.h>), then take that
 * as the address of the kernel pointer to "rom" (struct sunromvec).
 * Otherwise, nlist "romp" from the kernel.
 */
extern char *GetRomVer()
{
    static char			RomRev[16];
    struct nlist	       *nlptr;
#if	defined(HAVE_SUNROMVEC)
    static struct sunromvec	Rom;
    register char	       *p;
    register char	       *Addr;
    kvm_t		       *kd;
#if	!defined(romp)
    struct sunromvec	       *romp;

    if (!(kd = KVMopen()))
	return((char *) NULL);

    if ((nlptr = KVMnlist(kd, RomVecSYM, (struct nlist *)NULL)) == NULL)
	return((char *) NULL);

    if (CheckNlist(nlptr))
	return((char *) NULL);

    /*
     * Read the kernel pointer to the sunromvec structure.
     */
    if (KVMread(kd, (u_long) nlptr->n_value, (char *) &romp, 
		sizeof(romp))) {
	if (Debug) Error("Cannot read sunromvec pointer from kernel.");
	return((char *) NULL);
    }

#else	/* romp */

    if (!(kd = KVMopen()))
	return((char *) NULL);

#endif	/* romp */

    /*
     * Read the sunromvec structure from the kernel
     */
    /*SUPPRESS 25*/
    if (KVMread(kd, (u_long) romp, (char *) &Rom, sizeof(struct sunromvec))) {
	if (Debug) Error("Cannot read sunromvec from kernel.");
	return((char *) NULL);
    }

#if	!defined(romp)

    /*
     * XXX Hardcoded values
     */
    (void) sprintf(RomRev, "%d.%d", Rom.v_mon_id >> 16, Rom.v_mon_id & 0xFFFF);

#else	/* romp */

    /*
     * Read the version string from the address indicated by Rom.v_mon_id.
     * Read 1 byte at a time until '\0' is encountered.
     */
    p = RomRev;
    Addr = Rom.v_mon_id;
    do {
	if (KVMread(kd, (u_long) Addr++, p, 1))
	    break;
    } while (p < &RomRev[sizeof(RomRev)-1] && *p++);
    *p = C_NULL;

#endif	/* romp */

    KVMclose(kd);

#endif	/* HAVE_SUNROMVEC */

    return((RomRev[0]) ? RomRev : (char *) NULL);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.