File:  [MW Coherent from dump] / coherent / f / etc / conf / bin / src / sdev.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:40 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 "input.h"

#include "sdev.h"

#include <string.h>


LOCAL sdev_t  *	_sdevices;


/*
 * Macro to test whether some number ranges intersect; the arguments could be
 * either a pointer to a pair of longs or a pair of ints.
 */

#define	INTERSECT(left,right) \
	(((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__
LOCAL int (sdev_check_ioa) (sdev_t * check)
#else
LOCAL int
sdev_check_ioa ARGS ((check))
sdev_t	      *	check;
#endif
{
	sdev_t	      *	sdevp;
	int		share_conflict = 0;

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

	if (check->sd_ioa [1] == 0)
		return 0;

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

		if (sdevp->sd_ioa [1] == 0 ||
		    ! INTERSECT (sdevp->sd_ioa, check->sd_ioa))
			continue;

		if (mdev_flag (sdevp->sd_mdevp, MDEV_ALLOW_IOA_OVERLAP) &&
		    mdev_flag (check->sd_mdevp, MDEV_ALLOW_IOA_OVERLAP)) {
			if (share_conflict == 0)
				share_conflict  = 3;
		} else {
			report_conflict (check->sd_devname->s_data,
					 sdevp->sd_devname->s_data,
					 "IOA overlap");
			share_conflict = 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__
LOCAL int (sdev_check_cma) (sdev_t * check)
#else
LOCAL int
sdev_check_cma ARGS ((check))
sdev_t	      *	check;
#endif
{
	sdev_t	      *	sdevp;
	int		conflict = 0;

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

	if (check->sd_cma [1] == 0)
		return 0;

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

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

			report_conflict (check->sd_devname->s_data,
					 sdevp->sd_devname->s_data,
					 "CMA overlap");
			conflict = 1;
		}
	}

	return conflict;
}


/*
 * This function checks all existing 'sdevice' entries to see if the interrupt
 * vector 'vec' is in use by any of them.
 */

#ifdef	__USE_PROTO__
LOCAL int (sdev_check_vector) (sdev_t * check)
#else
LOCAL int
sdev_check_vector ARGS ((check))
sdev_t	      *	check;
#endif
{
	sdev_t	      *	sdevp;
	int		conflict = 0;

	if (check->sd_vector == 0)
		return 0;

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

		if (sdevp->sd_vector != check->sd_vector)
			continue;

		switch (sdevp->sd_itype) {

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

			/* FALL THROUGH */

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

			/* FALL THROUGH */

		case INT_PER_CHANNEL:
			report_conflict (check->sd_devname->s_data,
					 sdevp->sd_devname->s_data,
					 "Interrupt vector conflict");

			conflict = 1;
			break;

		case INT_NONE:
			break;
		}
	}

	return conflict;
}


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

#if	USE_PROTO
void (write_sdevice) (sdev_t * sdevp, input_t * input)
#else
void
write_sdevice ARGS ((sdevp, input))
sdev_t	      *	sdevp;
input_t	      *	input;
#endif
{
	if ((* input->in_filter) (input, "%s<1>%c<2>%d<3>%d<4>%d<5>%d<6>"
					 "0x%x<7>0x%x<8>0x%x<9>0x%x\n",
				  sdevp->sd_devname->s_data,
				  sdevp->sd_config ? 'Y' : 'N',
				  sdevp->sd_unit, 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]) < 0) {

		throw_error ("Output error");
	}
}


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

#if	USE_PROTO
LOCAL sdev_t * (read_sdevice) (input_t * input, lex_t * lexp, int * end_char)
#else
LOCAL sdev_t *
read_sdevice ARGS ((input, lexp, end_char))
input_t	      *	input;
lex_t	      *	lexp;
int	      *	end_char;
#endif
{
	VOLATILE int	ch = '\n';
	sdev_t * VOLATILE sdevp;
	sdev_t	      *	scan;
	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) {

			free (sdevp);
			sdevp = 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);

		/*
		 * 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');

		/*
		 * Read the unit number; since we match both the unit number
		 * and the device name when changing an entry, we only allow
		 * one sdevice entry per unit/device combination.
		 */

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

		for (scan = _sdevices ; scan != NULL ; scan = scan->sd_next)
			if (scan->sd_devname == sdevp->sd_devname &&
			    scan->sd_unit == sdevp->sd_unit) {
				throw_error ("Only one sdevice entry per device unit allowed");
			}


		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);

		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");

		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");

		ch = expect_eol (input, lexp, ch);
	} else {

		free (sdevp);
		CHAIN_ERROR (err);
	}

at_eof:
	POP_HANDLER (err);

	* end_char = ch;
	return sdevp;
}


/*
 * This function is passed as a pointer to for_all_mdevices () to aid in
 * checking to see that enabled devices have unique major numbers.
 */

#if	USE_PROTO
LOCAL void check_major (mdev_t * mdevp, mdev_t * enabled)
#else
LOCAL void
check_major (mdevp, enabled)
mdev_t	      *	mdevp;
mdev_t	      *	enabled;
#endif
{
	CONST char    *	msg;

	if (mdevp == enabled || enabled->md_configure != MD_ENABLED)
		return;

	if (mdev_flag (enabled, MDEV_BLOCK) && mdev_flag (mdevp, MDEV_BLOCK))
		if (INTERSECT (mdevp->md_blk_maj, enabled->md_blk_maj)) {
			msg = "Block major number overlap";
conflict:
			report_conflict (mdevp->md_devname->s_data,
					 enabled->md_devname->s_data,
					 msg);
			mdevp->md_configure = MD_DISABLED;
			return;
		}

	if (mdev_flag (enabled, MDEV_CHAR) && mdev_flag (mdevp, MDEV_CHAR))
		if (INTERSECT (mdevp->md_chr_maj, enabled->md_chr_maj)) {
			msg = "Character major number overlap";
			goto conflict;
		}

	if (mdev_flag (enabled, MDEV_COHERENT) &&
	    mdev_flag (mdevp, MDEV_COHERENT))
		if (INTERSECT (mdevp->md_chr_maj, enabled->md_chr_maj)) {
			msg = "Major number overlap";
			goto conflict;
		}
}


/*
 * This function is passed as a parameter to read_dev_string () to read an
 * 'mtune' entry (usually a program argument) and hook it into a global list.
 */

#if	USE_PROTO
LOCAL int _read_sdev_string (input_t * input, lex_t * lexp,
			     sdev_t ** sdlistp)
#else
LOCAL int
_read_sdev_string (input, lexp, sdlistp)
input_t	      *	input;
lex_t	      *	lexp;
sdev_t	     **	sdlistp;
#endif
{
	sdev_t	      *	sdevp;
	int		ch;

	if ((sdevp = read_sdevice (input, lexp, & ch)) != NULL) {
		sdevp->sd_next = * sdlistp;
		* sdlistp = sdevp;
	}

	return ch;
}


/*
 * This function is used by _read_sdevice_file () to handle the details of
 * linking an 'sdevice' entry into the global data structures and checking the
 * data consistency.
 */

#if	USE_PROTO
LOCAL void (link_sdevice) (sdev_t * sdevp, input_t * input)
#else
LOCAL void
link_sdevice ARGS ((sdevp, input))
sdev_t	      *	sdevp;
input_t	      *	input;
#endif
{
	CONST char    *	errmsg;

	if ((sdevp->sd_mdevp = find_mdev (sdevp->sd_devname)) == NULL) {
		errmsg = "device name does not match any master device entry";
have_error:
		report_error (errmsg);
		free (sdevp);
		return;
	}

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

	if (sdevp->sd_unit < sdevp->sd_mdevp->md_minor_min ||
	    sdevp->sd_unit > sdevp->sd_mdevp->md_minor_max) {
		errmsg = "unit number out of range for device";
		goto have_error;
	}

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

	switch (sdev_check_ioa (sdevp)) {
	case 3:
	case 0:
		/*
		 * Every device we might conflict with has the 'O' flag set
		 * and so do we, or there is no conflict.
		 */
		break;

	default:
		free (sdevp);
    		return;
	}

	if (sdevp->sd_cma [1] > 0 &&
	    ! mdev_flag (sdevp->sd_mdevp, MDEV_HARDWARE)) {
		errmsg = "software-only device cannot have controller memory";
		goto have_error;
	}

	if (sdev_check_cma (sdevp) != 0) {
		free (sdevp);
		return;
	}

	if (sdevp->sd_itype > 0) {

		if (! mdev_flag (sdevp->sd_mdevp, MDEV_HARDWARE)) {
			errmsg = "software-only device cannot have a vector";
			goto have_error;
		}

		sdevp->sd_mdevp->md_interrupt = 1;
	}


	if (sdev_check_vector (sdevp)) {
		free (sdevp);
		return;
	}


	/*
	 * After we have passed all the checks above, we commit to linking the
	 * entry in... no more gotos after this point!
	 */

	sdevp->sd_next = _sdevices;
	_sdevices = sdevp;

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

	if (sdevp->sd_config) {
		/*
		 * Check enabled devices for major number clashes.
		 */

		for_all_mdevices ((miter_t) check_major,
				  sdevp->sd_mdevp);			
		sdevp->sd_mdevp->md_configure = MD_ENABLED;
	}

	write_sdevice (sdevp, input);
}


/*
 * This function is passed as a pointer to read_dev_file () in order to read
 * an 'sdevice' entry and link it into a global chain.
 */

#if	USE_PROTO
LOCAL int _read_sdevice_file (input_t * input, lex_t * lexp,
			      sdev_t ** changes)
#else
LOCAL int
_read_sdevice_file ARGS ((input, lexp, changes))
input_t	      *	input;
lex_t	      *	lexp;
sdev_t	     **	changes;
#endif
{
	sdev_t	      *	sdevp;
	int		ch;

	if ((sdevp = read_sdevice (input, lexp, & ch)) == NULL) {
		if (ch == READ_EOF) {
			/*
			 * Blow remaining changes out as new entries.
			 */

			while ((sdevp = * changes) != NULL) {
				* changes = sdevp->sd_next;
				link_sdevice (sdevp, input);
			}
		}
		return ch;
	}


	/*
	 * Link the newly-read entry into the global lists.
	 */

	if (changes) {
		sdev_t	     **	scan;

		for (scan = changes ; * scan != NULL ;
		     scan = & (* scan)->sd_next) {

			if ((* scan)->sd_devname == sdevp->sd_devname &&
			    (* scan)->sd_unit == sdevp->sd_unit) {
				/*
				 * Our current entry is being replaced by a
				 * new one.
				 */

				free (sdevp);

				if (report_mode ())
					return ch;

				sdevp = * scan;
				* scan = sdevp->sd_next;
				break;
			}
		}
	}

	link_sdevice (sdevp, input);
	return ch;
}


/*
 * Read in an "mtune" entry from a string and add it to a list.
 */

#if	USE_PROTO
void read_sdev_string (CONST char * string, VOID * extra)
#else
void
read_sdev_string (string, extra)
CONST char    *	string;
VOID	      *	extra;
#endif
{
	read_dev_string (string, (dev_func_p) _read_sdev_string, extra);
}


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

#if	USE_PROTO
void (read_sdev_file) (CONST char * inname, CONST char * outname, VOID * extra)
#else
void
read_sdev_file ARGS ((inname, outname, extra))
CONST char    *	inname;
CONST char    *	outname;
VOID	      *	extra;
#endif
{
	read_dev_file (inname, outname, (dev_func_p) _read_sdevice_file,
		       extra);
}


/*
 * 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.
 *
 * The return value is the number of items in the target list.
 */

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

#if	USE_PROTO
int (sdev_sort) (sdev_t ** sdlistp, sdev_t ** sdendp, ssel_t selpred,
		 scmp_t cmppred, size_t ptroff)
#else
int
sdev_sort ARGS ((sdlistp, sdendp, selpred, cmppred, ptroff))
sdev_t        **sdlistp;
sdev_t	      **sdendp;
ssel_t		selpred;
scmp_t		cmppred;
size_t		ptroff;
#endif
{
	sdev_t	      *	scan;
	sdev_t	      *	next;
	int		count;

	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.
	 */

	* sdlistp = NULL;
	if (sdendp != NULL)
		* sdendp = NULL;

	for (count = 0, scan = _sdevices ; 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 ; 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 = scan;
		else
			LINK (prev, ptroff) = scan;

		if ((LINK (scan, ptroff) = findpos) == NULL && sdendp != NULL)
			* sdendp = scan;

		count ++;
	}

	return count;
}

unix.superglobalmegacorp.com

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