File:  [MW Coherent from dump] / coherent / b / STREAMS / conf_79 / sdev.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:36 2019 UTC (7 years, 2 months ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/*
 *-IMPORTS:
 *	<sys/compat.h>
 *		LOCAL
 *		USE_PROTO
 *		ARGS ()
 *	<stdlib.h>
 *		NULL
 *		free ()
 *		malloc ()
 *	"ehand.h"
 *		ehand_t
 *		CHAIN_ERROR ();
 *		POP_HANDLER ();
 *		PUSH_HANDLER ();
 *		throw_error ();
 *	"mdev.h"
 *		mdev_t
 *		check_errs ()
 *		find_mdev ()
 *	"symbol.h"
 *		LEX_WILD
 *		SIGNED
 *		SYM_EOF
 *		UNSIGNED
 *		read_dev_file ()
 *		read_number ()
 *		read_symbol ()
 */

#include <sys/compat.h>
#include <stdlib.h>

#include "ehand.h"
#include "mdev.h"
#include "symbol.h"
#include "read.h"
#include "lex.h"

#include "sdev.h"

#include <string.h>


LOCAL sdev_t  *	sdevices;


/*
 * Simple local function for testing to see if IOA or CMA ranges intersect.
 */

#ifdef	USE_PROTO
LOCAL int (intersect) (CONST unsigned long * left, CONST unsigned long * right)
#else
LOCAL int
intersect ARGS ((left, right))
CONST int     *	left;
CONST int     *	right;
#endif
{
	return (left [0] >= right [0] && left [0] <= right [1]) ||
	       (left [1] >= right [0] && left [1] <= right [1]);
}


/*
 * This function checks all existing 'sdevice' entries to see if there is an
 * IOA conflict.
 *
 * The value returned is the same value that would be returned by the matching
 * check performed by the idcheck(1M) utility, namely:
 *	0 if no conflict.
 *	1 if there is a conflict.
 *	3 if there is a conflict, but the 'mdevice' entry has the O flag set.
 *
 * As usual, the specification does not take into account the possibility that
 * an IOA range may conflict with several devices with a different setting of
 * the 'O' flag. This routine returns 3 if and only if all devices which have
 * IOA conflicts have the 'O' flag set.
 *
 * If "who" is non-NULL, it will be written with the address of the sdevice
 * entry which caused the conflict which caused the non-zero return.
 */

#ifdef	__USE_PROTO__
int (sdev_check_ioa) (CONST ioa_t * check, sdev_t ** who)
#else
int
sdev_check_ioa ARGS ((check, who))
CONST int     *	check;
sdev_t	     **	who;
#endif
{
	sdev_t	      *	sdevp;
	int		share_conflict = 0;

	if (check == NULL)
		throw_error ("check argument is NULL in sdev_check_ioa ()");

	if (check [1] == 0)
		return 0;

	for (sdevp = sdevices ; sdevp != NULL ; sdevp = sdevp->sd_next) {

		if (sdevp->sd_ioa [1] > 0 &&
		    intersect (sdevp->sd_ioa, check)) {

			if (mdev_flag (sdevp->sd_mdevp,
				       MDEV_ALLOW_IOA_OVERLAP)) {
				/*
				 * Since multiple entries can cause a shared
				 * IOA conflict, return the first.
				 */

				if (who != NULL && share_conflict == 0)
					* who = sdevp;

				share_conflict  = 3;
			} else {
				/*
				 * Since this 'sdevice' entry caused a fatal
				 * conflict, we always return that one.
				 */

				if (who != NULL)
					* who = sdevp;

				return 1;
			}
		}
	}

	return share_conflict;
}


/*
 * This function checks all existing 'sdevice' entries for a CMA conflict.
 *
 * The value returned will be 1 if there is a conflict and 0 if no conflict
 * occurs. If the "who" parameter is non-NULL, it will be written with the
 * address of the 'sdevice' entry that caused the conflict (if any).
 */

#ifdef	__USE_PROTO__
int (sdev_check_cma) (CONST cma_t * check, sdev_t ** who)
#else
int
sdev_check_cma ARGS ((check, who))
CONST int     *	check;
sdev_t	     **	who;
#endif
{
	sdev_t	      *	sdevp;

	if (check == NULL)
		throw_error ("check argument is NULL in sdev_check_cma ()");

	if (check [1] == 0)
		return 0;

	for (sdevp = sdevices ; sdevp != NULL ; sdevp = sdevp->sd_next) {

		if (sdevp->sd_cma [1] > 0 &&
		    intersect (sdevp->sd_cma, check)) {

			/*
			 * Since this 'sdevice' entry caused a fatal
			 * conflict, we always return that one.
			 */

			if (who != NULL)
				* who = sdevp;

			return 1;
		}
	}

	return 0;
}


/*
 * This function checks all existing 'sdevice' entries to see if the interrupt
 * vector 'vec' is in use by any of them. If any other devices are using the
 * vector, the lowest non-zero 'type' value specified by any of them is
 * returned. If the "who" entry is non-NULL, the address of the first entry
 * responsible for the return value is returned.
 */

#ifdef	__USE_PROTO__
int (sdev_check_vector) (int vec, sdev_t ** who)
#else
int
sdev_check_vector ARGS ((vec, who))
int		vec;
sdev_t	     **	who;
#endif
{
	sdev_t	      *	sdevp;
	int		itype = MAX_ITYPE + 1;

	if (vec == 0)
		return 0;

	for (sdevp = sdevices ; sdevp != NULL ; sdevp = sdevp->sd_next) {

		if (sdevp->sd_vector == vec && sdevp->sd_itype < itype) {

			if (who != NULL)
				* who = sdevp;

			itype = sdevp->sd_itype;
		}
	}

	return itype <= MAX_ITYPE ? itype : 0;
}


/*
 * Read a line from an "sdevice" file.
 *
 * This code is really messy. My apologies.
 */

#ifdef	USE_PROTO
LOCAL int (read_sdevice) (input_t * input, lex_t * lexp)
#else
LOCAL int
read_sdevice ARGS ((input, lexp))
input_t	      *	input;
lex_t	      *	lexp;
#endif
{
	int		ch = '\n';
	sdev_t	      *	sdevp;
	sdev_t	      *	serr;
	ehand_t		err;
	lex_t		yesno = { NULL, NULL, "-", "ynYN", LEX_WILD };
	symbol_t      *	config_flag;

	yesno.l_prev = lexp;

	if ((sdevp = (sdev_t *) malloc (sizeof (* sdevp))) == NULL)
		throw_error ("out of memory in read_sdevice ()");

	if (PUSH_HANDLER (err) == 0) {
		/*
		 * If the first thing on the line is EOF, that's not an error,
		 * just bail out.
		 */

		ch = read_symbol (input, lexp, & sdevp->sd_devname);

		if (sdevp->sd_devname == NULL)
			goto at_eof;

		check_not_eol (ch);

		if (sdevp->sd_devname->s_size > MAX_DEVNAME)
			throw_error ("device name must be <= %d characters",
				     MAX_DEVNAME);

		if ((sdevp->sd_mdevp = find_mdev (sdevp->sd_devname)) == NULL)
			throw_error ("device name does not match any master device entry");

		if (mdev_flag (sdevp->sd_mdevp, MDEV_ONE_SDEVICE) &&
		    sdevp->sd_mdevp->md_sdevices != NULL)
			throw_error ("only one sdevice entry permitted for this device");


		/*
		 * We read the yes/no field as a symbol, since most of the
		 * entries will be identical.
		 */

		ch = read_symbol (input, & yesno, & config_flag);
		check_not_eol (ch);

		if (config_flag->s_size > 1)
			throw_error ("a single Y or N is required");

		sdevp->sd_config = (config_flag->s_data [0] == 'y' ||
				    config_flag->s_data [0] == 'Y');


		ch = read_uints (input, lexp, & sdevp->sd_unit, NO_RANGE);
		check_not_eol (ch);

		if (sdevp->sd_unit < sdevp->sd_mdevp->md_minor_min ||
		    sdevp->sd_unit > sdevp->sd_mdevp->md_minor_max)
			throw_error ("unit number out of range for device");

		ch = read_uints (input, lexp, & sdevp->sd_ipl, NO_RANGE);
		check_not_eol (ch);

		if (sdevp->sd_ipl > MAX_IPL)
			throw_error ("IPL must be in the range 0 through %d",
				     MAX_IPL);

		ch = read_uints (input, lexp, & sdevp->sd_itype, NO_RANGE);
		check_not_eol (ch);

		if (sdevp->sd_itype > MAX_ITYPE)
			throw_error ("interrupt type must be in the range 0 to %d",
				     MAX_ITYPE);

		ch = read_uints (input, lexp, & sdevp->sd_vector, NO_RANGE);
		check_not_eol (ch);

		/*
		 * The clock device is special-cased as the only thing that
		 * can use vector 0.
		 */

		if (strcmp (sdevp->sd_devname->s_data, "clock") != 0 &&
		    ((sdevp->sd_ipl > 0 && sdevp->sd_vector == 0) ||
		     (sdevp->sd_ipl == 0 && sdevp->sd_vector > 0))) {
			throw_error ("IPL and vector must both be either zero or non-zero");
		}

		if (sdevp->sd_vector > MAX_VECTOR)
			throw_error ("vector numbers run from 0 (no vector) through %d",
				     MAX_VECTOR);

		switch (sdev_check_vector (sdevp->sd_vector, & serr)) {

		case INT_SHAREABLE:
			if (sdevp->sd_itype == INT_SHAREABLE)
				break;

			/* FALL THROUGH */

		case INT_PER_DEVICE:
			if (serr->sd_devname == sdevp->sd_devname &&
			    sdevp->sd_itype >= INT_PER_DEVICE)
				break;

			/* FALL THROUGH */

		case INT_PER_CHANNEL:
			throw_error ("vector conflict with device %s",
				     serr->sd_devname->s_data);
			break;

		case INT_NONE:
			break;
		}

		ch = read_ulongs (input, lexp, & sdevp->sd_ioa [0], NO_RANGE);
		check_not_eol (ch);

		ch = read_ulongs (input, lexp, & sdevp->sd_ioa [1], NO_RANGE);
		check_not_eol (ch);

		if (sdevp->sd_ioa [0] > sdevp->sd_ioa [1])
			throw_error ("lower bound of address range higher than upper bound");

		if (sdevp->sd_ioa [1] > 0 &&
		    ! mdev_flag (sdevp->sd_mdevp, MDEV_HARDWARE))
			throw_error ("software-only device cannot access I/O ports");

		switch (sdev_check_ioa (sdevp->sd_ioa, & serr)) {

		case 3:
			if (mdev_flag (sdevp->sd_mdevp,
				       MDEV_ALLOW_IOA_OVERLAP)) {
				/*
				 * Every device we might conflict with has the
				 * 'O' flag set and so do we.
				 */

				break;
			}

			/* FALL THROUGH */

		default:
			throw_error ("IOA conflict with device %s",
				     serr->sd_devname->s_data);
			break;

		case 0:
			break;
		}

		ch = read_ulongs (input, lexp, & sdevp->sd_cma [0], NO_RANGE);
		check_not_eol (ch);

		ch = read_ulongs (input, lexp, & sdevp->sd_cma [1], NO_RANGE);

		if (sdevp->sd_cma [0] > sdevp->sd_cma [1])
			throw_error ("lower bound of address range higher than upper bound");

		if (sdevp->sd_cma [1] > 0 &&
		    ! mdev_flag (sdevp->sd_mdevp, MDEV_HARDWARE))
			throw_error ("software-only device cannot access controller memory");

		if (sdev_check_cma (sdevp->sd_cma, & serr) != 0)
			throw_error ("CMA conflict with device %s",
				     serr->sd_devname->s_data);

		ch = expect_eol (input, lexp, ch);


		/*
		 * Having passed all the reasonableness checks, we link the
		 * new entry into the chain.
		 */

		sdevp->sd_next = sdevices;
		sdevices = sdevp;

		sdevp->sd_mnext = sdevp->sd_mdevp->md_sdevices;
		sdevp->sd_mdevp->md_sdevices = sdevp;

		if (sdevp->sd_mdevp->md_configure == MD_INSTALLABLE &&
		    sdevp->sd_config)
			sdevp->sd_mdevp->md_configure = MD_ENABLED;

		if (sdevp->sd_itype > 0) {

			if (! mdev_flag (sdevp->sd_mdevp, MDEV_HARDWARE))
				throw_error ("software-only device cannot have a vector");

			sdevp->sd_mdevp->md_interrupt = 1;
		}
	} else {

		free (sdevp);
		CHAIN_ERROR (err);
	}

at_eof:
	POP_HANDLER (err);
	return ch;
}


#if 0
/*
 * Regenerate an 'sdevice' entry from the stored record.
 */

#ifdef	USE_PROTO
void (write_sdevice) (sdev_t * sdevp, FILE * out)
#else
void
write_sdevice ARGS ((sdevp, out))
sdev_t	      *	sdevp;
FILE	      *	out;
#endif
{
	(void) fprintf (out, "%-10s %c %-3d %-3d %-3d 0x%-6x 0x%-6x 0x%-10x 0x%-10x\n",
			sdevp->sd_devname->s_data, sdevp->sd_config ? 'Y' : 'N',
			sdevp->sd_ipl, sdevp->sd_itype, sdevp->sd_vector,
			sdevp->sd_ioa [0], sdevp->sd_ioa [1], sdevp->sd_cma [0],
			sdevp->sd_cma [1]);
}
#endif


/*
 * Suck in a complete 'sdevice' file.
 */

#ifdef	USE_PROTO
void (read_sdev_file) (CONST char * name)
#else
void
read_sdev_file ARGS ((name))
CONST char    *	name;
#endif
{
	read_dev_file (name, read_sdevice);
}


/*
 * Generic insertion sort algorithm for "sdevice" entries based on a
 * selection predicate and a comparison predicate.
 *
 * So that this can be a reasonably generic function, we pass it the internal
 * offset of the "sdev_t *" member of the "sdevice" structure which will be
 * used to link together the sorted entries.
 */

#define	LINK(sdevp,off)		(* (sdev_t **) ((char *) (sdevp) + off))

#ifdef	USE_PROTO
void (sdev_sort) (sdlist_t * sdlistp, ssel_t selpred, scmp_t cmppred,
		  size_t ptroff)
#else
void
sdev_sort ARGS ((sdlistp, selpred, cmppred, ptroff))
sdlist_t	      *	sdlistp;
ssel_t		selpred;
scmp_t		cmppred;
size_t		ptroff;
#endif
{
	sdev_t	      *	scan;
	sdev_t	      *	next;

	if (sdlistp == NULL || ptroff > sizeof (sdev_t))
		throw_error ("bogus parameters to sdev_sort ()");


	/*
	 * We'll just insert each selected member of the total list of
	 * mdevices into the output list in order by running down the output
	 * list until we compare true.
	 *
	 * We fetch "scan" before initializing the output list in case we are
	 * sorting the master device list.
	 */

	scan = sdevices;

	sdlistp->sdl_first = sdlistp->sdl_last = NULL;
	sdlistp->sdl_count = 0;

	for (; scan != NULL ; scan = next) {
		sdev_t	      *	findpos;
		sdev_t	      *	prev;

		/*
		 * We get the "next" entry now in case we are sorting the
		 * master list. We allow a "selpred" of NULL to select all
		 * the entries.
		 */

		next = scan->sd_next;

		if (selpred != NULL && (* selpred) (scan) == 0)
			continue;


		/*
		 * Now attempt to find the right place for the new entry and
		 * insert it there.
		 */

		prev = NULL;

		for (findpos = sdlistp->sdl_first ; findpos != NULL ;
		     findpos = LINK ((prev = findpos), ptroff)) {
			/*
			 * A "cmppred" that is NULL means that the order of
			 * output entries is irrelevant.
			 */

			if (cmppred == NULL ||
			   (* cmppred) (findpos, scan) == 0)
				break;
		}

		if (prev == NULL)
			sdlistp->sdl_first = scan;
		else
			LINK (prev, ptroff) = scan;

		if ((LINK (scan, ptroff) = findpos) == NULL)
			sdlistp->sdl_last = scan;

		sdlistp->sdl_count ++;
	}
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.