/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: devcon.c 1.47 90/04/27 $"

#include <stdio.h>
#include <ctype.h>
#include "config.h"
#include "symbols.h"			/* symbol table definitions */
#include "allocate.h"			/* perm_alloc(), free_list() */
#include "expression.h"			/* expression definitions */

extern	char *strcat();
extern	char *strncpy();
extern	int strcmp();

#define	DEVNM_HASHENTS	50	/* number of hash entries for device names */
static	int device_names_initialized=0;
				/* device symbol table initialized flag */
static	opaque_ptr device_names;/* device symbol table */

struct	device 	*cur;		/* current device */
struct	device 	*can_cur;	/* previous device structure */

struct	connect	*busts;		/* start of linked list of buss types */
struct	connect	*busts_tail;	/* end of linked list of busses */

/*
 * these on "on a bus" devices (device drivers and bus drivers)
 */
struct	connect	*busds;		/* start of linked list of buss drivers */
struct	connect	*busds_tail;	/* end of linked list of busses drivers */

struct	connect	*ctab_tail;	/* end of linked list of all drivers */

struct	list_head modtab;	/* for struct fmodsw[] */
struct	list_head driver_module;/* for struct driver module */
struct	list_head inits;	/* for struct io_init[] */
struct	list_head busses;	/* for struct busses */
struct	list_head on_devices;	/* for struct gen_devices */
struct	list_head pseudos;	/* for pseudo devices */
struct	list_head pseudo_x;	/* for pseudo devices */
struct	list_head cstreams;	/* for stream devices */

struct	list_head init_list;	/* initialization functions */

/*
 * This is the device_entry table used by pr_bdev() and pr_cdev()
 * The following manifests must correspond to the device driver
 * entry points named below.
 */
#define	L_PROBE	0
#define L_INIT	1
#define L_INTR	2
#define L_POLL	3
#define L_OPEN	4
#define L_CLOSE	5
#define L_READ	6
#define L_WRITE	7
#define L_IOCTL	8
#define L_STRAT	9
#define L_MINIP	10
#define L_PSIZE 11
#define L_PRINT	12
#define L_MMAP	13

struct driver_entry {
	int entry_num;			/* entry point number */
	char *entry_name;		/* entry point name */
	char *entry_default;		/* entry default */
	char *entry_type;		/* entry type */
} driver_entry[] = {
	{ L_PROBE,	"_probe" ,	"NULL",		"int" },
	{ L_INIT,	"_init" ,	"NULL",		"void" },
	{ L_INTR,	"_intr" ,	"NULL",		"void" },
	{ L_POLL,	"_poll" ,	"NULL",		"int" },
	{ L_OPEN,	"open" ,	"nulldev",	"int" },
	{ L_CLOSE,	"close" ,	"nulldev",	"int" },
	{ L_READ,	"read" ,	"nulldev",	"int" },
	{ L_WRITE,	"write" ,	"nulldev",	"int" },
	{ L_IOCTL,	"ioctl" ,	"nodev",	"int" },
	{ L_STRAT,	"strat" ,	"nodev",	"int" },
	{ L_MINIP,	"minphys" ,	"nulldev",	"int" },
	{ L_PSIZE,	"size" ,	"nulldev",	"int" },
	{ L_PRINT,	"print" ,	"nulldev",	"int" },
	{ L_MMAP,	"mmap" ,	"nulldev",	"int" },
	{ 0,		(char*)NULL,	(char*)NULL,	(char*)NULL }
};

static int	nif = 8;
static char 	*biface[8] = {
	"probe_setup",
	"alloc_maps",
	"dma_setup",
	"dma_remap",
	"dma_done",
	"dma_flush",
	"intr_setup",
	"ack_int"
};

struct	device	*Bdevp[MAX_BDEV];
struct	device	*Cdevp[MAX_CDEV];
struct	device	*Busp[MAX_BUS];
struct	device	*Fsp[MAX_FS];
int	Syscalls[MAX_SYSCALL];

static	int streams_module_count;

struct	connect	*findcon();
struct	connect	*findncon();
struct	device	*finddev();

/*
 * initialise a new device structure and set "cur" to point to it;
 * Called before yyparse() on a config file.
 */

new_dev(name)
	char			*name;
{
	extern	char		*sname();
	extern	struct	device	*finddev();
	char			*simple_name;

	simple_name = sname(name);
	if ((cur = finddev(simple_name)) == (struct device *)NULL) {
		if (DEBUG_LEVEL(12))
			printf("New potential device %s\n", name);
		if (can_cur != (struct device *)NULL) {
			cur = can_cur;
		} else {
			cur = (struct device *)
				perm_alloc(sizeof(struct device));
		}
		dev_init(cur, simple_name);
	} else {
		if (DEBUG_LEVEL(12))
			printf("Old device %s\n",name);
	}	
}

/*
* Initialise a device structure.
*/
static
dev_init(d, simple_name)
	register struct	device	*d;
	char			*simple_name;
{
	d->d_type = 0;
	d->d_on_bus = (struct device **)NULL;
	d->d_bus = (struct device **)NULL;
	d->d_bus_own = (struct device *)NULL;
	d->d_conn = (struct connect *)NULL;
	if (strlen(simple_name) > MAX_DEVNAMELEN) {
		yyerror(
		"Device or bus name [%s] exceeds max. allowable length of %d.",
			simple_name, MAX_DEVNAMELEN);
	}
	(void)strncpy(d->d_name, simple_name, MAX_DEVNAMELEN);
	(void)strncpy(d->d_prefix, simple_name, MAX_DEVNAMELEN);
	to_upper(strncpy(d->d_uprefix, simple_name, MAX_DEVNAMELEN));
	d->d_aliases.l_head = NULL;
	d->d_aliases.l_last = NULL;
	d->d_busno = UNKNOWN;
	d->d_numdev = 1;
	d->d_maxunit = 1;
	d->d_minunit = 0;
	d->d_entry = 0;
	d->d_block = UNKNOWN;
	d->d_character = UNKNOWN;
	d->d_flags = 0;
}

/*
 * The config was not for a device so cancel it (re use in new_dev).
 */
can_dev()
{
	if (cur->d_aliases.l_head != NULL) {
		free_list(cur->d_aliases.l_head);
	}
	can_cur = cur;
	cur = (struct device *)NULL;
}

/*
 * Called with a list of device driver entry points.
 */
dev_entry(entry_points)
struct list *entry_points;
{
	static int entry_names_initialized = 0;	/* initialized flag */
	static opaque_ptr entry_names;		/* device driver entry names */
	register struct driver_entry *dep;
	register struct list *entryp;
	opaque_ptr res;

	if (!entry_names_initialized) {
		entry_names_initialized = 1;
		entry_names = create_symtab( sizeof(driver_entry)/sizeof(*dep));
		dep = &driver_entry[0];
		while (dep->entry_name) {
			res = insert_symtab(entry_names, dep->entry_name,
					    (opaque_ptr)dep);
			if (res != (opaque_ptr)NULL) {
				fatal("internal error in dev_entry\n");
				exit(1);
			}
			dep++;
		}
	}
	for (entryp = entry_points;
	     entryp != (struct list *)NULL; entryp = entryp->l_next) {
		dep = (struct driver_entry *)
		       search_symtab(entry_names, entryp->l_text);
		if (dep == (struct driver_entry *)NULL) {
			yyerror("non-existent driver entry \"%s\" declared.\n",
				entryp->l_text);
		} else {		/* include entry point in set */
			cur->d_entry |= (1L << dep->entry_num);
		}
	}
	free_list(entry_points);
}

/*
 * Called at end of parsing a mod_config file.
 * check for validity.
 */
boolean
add_dev(select)
boolean select;				/* parameter select flag */
{
	char numdev_par[1024];		/* "NUMDEV" parameter for device */
	char numdev_par_descr[1024];	/* "NUMDEV" parameter description */

	/*
	 *  For implicit parameter name (in case we need it).
	 */
	if (Vers.version >= 107) {
		(void)sprintf(numdev_par, "_%s_NUMDEV", cur->d_uprefix);
		(void)sprintf(numdev_par_descr,
			      "Number of %s(%s) devices.",
				cur->d_name, cur->d_uprefix);
	}
	/*
	 * find connected device information
	 */
	cur->d_conn = findcon(cur->d_name);
	if (cur->d_type & T_PSEUDO) {
		if (Vers.version >= 107 && cur->d_conn->c_d_val != UNKNOWN) {
			declare_par(numdev_par, LOWEST_VISIBILITY-1, select,
				    "int", numdev_par_descr, FALSE);
		}
	} else if (cur->d_type & (T_BLOCK|T_CHAR)) {
		/*
		 *  Check if autoconfigured.
		 */
		if (cur->d_conn == (struct connect *)NULL) {
			fflush(stdout);		/* In case stdout > stderr */
			fprintf(stderr,
		"WARNING: %s: driver \"%s\" is not configured on any bus.\n",
				Ffile,cur->d_name);
			Warning = TRUE;
			/* cancel configuration */
			can_dev();
			return(FALSE);
		}
		if (Vers.version >= 107) {
			declare_par(numdev_par, LOWEST_VISIBILITY-1, select,
				    "int", numdev_par_descr, FALSE);
		}
	}
	/*
	 *  All the information in "cur" is now filled in
	 */
	if (!device_names_initialized) {
		device_names_initialized = 1;
		device_names = create_symtab(DEVNM_HASHENTS);
	}
	if (insert_symtab(device_names,
			  cur->d_name, (opaque_ptr)cur) != (opaque_ptr)NULL) {
		fatal("internal error in add_dev()\n");
		exit(1);
	}
	cur->d_next = dtab;
	dtab = cur;
	can_cur = (struct device *)NULL;
	if (DEBUG_LEVEL(12))
		printf ("New device: %s\n", cur->d_name);
	return(TRUE);
}

add_streamm_aliases(sm_aliases) 
struct list_head sm_aliases;
{
	register struct list *listp, *clistp;

	/*
	 *  Make sure to squelch duplicate alias declarations.
	 */
	listp = sm_aliases.l_head;
	while (listp != (struct list *)NULL) {	/* Loop over declarations */
		clistp = cur->d_aliases.l_head;	/* Is it already there? */
		while (clistp != (struct list *)NULL) {
			if (strcmp (clistp->l_text, listp->l_text) == 0) {
				fflush(stdout);	/* In case stdout > stderr */
				fprintf(stderr, "WARNING: streams module ");
				fprintf(stderr, "alias: [%s] already\n",
					listp->l_text);
				fprintf(stderr, "         declared for ");
				fprintf(stderr, "streams module: [%s].\n",
					cur->d_prefix);
				Warning = TRUE;
				break;
			}
		}
		if (clistp == (struct list *)NULL) {	/* not in list */
			if (cur->d_aliases.l_head == (struct list *)NULL)
				cur->d_aliases.l_head = listp;
			else
				cur->d_aliases.l_last->l_next = listp;
			cur->d_aliases.l_last = listp;
		}
		clistp = listp;
		listp = listp->l_next;
		clistp->l_next = (struct list *)NULL;
	}
}

add_init(init_funcs) 
struct list_head init_funcs;
{
	register struct list *ifunc;

	if (init_list.l_head == (struct list *)NULL)
		init_list.l_head = init_funcs.l_head;
	else
		init_list.l_last->l_next = init_funcs.l_head;
	if (DEBUG_LEVEL(12)) {
		for (ifunc = init_funcs.l_head;
		     ifunc != (struct list *)NULL; ifunc = ifunc->l_next) {
			printf("Init function: [%s]\n", ifunc->l_text);
		}
	}
	init_list.l_last = init_funcs.l_last;
}

/*
 * Allocate a list of "on_bus" pointers and fill it in.
 * Then connect this driver to that bus.
 */
add_on_bus(buslist, nbusses)
	struct	list	*buslist;
	int		nbusses;
{
	extern	struct	device	*finddev();
	register struct	list *busp;
	struct	device	**bl;
	struct	device	*dev;
	int		bus;

	bl = (struct device **)
	      perm_alloc((nbusses+1) * sizeof(struct device *));
	cur->d_on_bus = bl;

	if (DEBUG_LEVEL(8))
		printf("Add_on_bus:\"%s\" %d buses\n", cur->d_name, nbusses);

	/*
	 * check for bus in list.
	 */
	bus = 0;
	for (busp = buslist; busp != (struct list *)NULL; busp = busp->l_next) {
		if (finddev(busp->l_text) != (struct device *)NULL)
			bus = 1;
	}
	/*
	 * now check them.
	 */
	for (busp = buslist; busp != (struct list *)NULL; busp = busp->l_next) {
		if ((dev = finddev(busp->l_text)) == (struct device *)NULL) {
			if (bus == 0) {
				yyerror("cannot find bus \"%s\" ",
					busp->l_text);
			}
		} else {
			*bl++ = dev;
		}
	}
	*bl = (struct device *)NULL;
	free_list(buslist);
}


/*
 * Allocate a list of bus pointers.
 * This device is a driver that supports one or more buss types
 * other busses may be "on" this buss device.
 */
add_bus(busdevs, nbusses)
	struct	list	*busdevs;
	int		nbusses;
{
	extern	struct	device	*finddev();
	register struct	list	*busdevp;
	register struct	device	*dev;
	struct	device		**bl;

	bl = (struct device **)
	      perm_alloc((nbusses+1) * sizeof(struct device *));
	cur->d_bus = bl;
	if (DEBUG_LEVEL(8))
		printf("Add_bus:\"%s\":#%d %d buses\n", cur->d_name,
				cur->d_busno, nbusses);
	/*
	 * The bus may be the module being parsed on a new one
	 */
	for (busdevp = busdevs;
	     busdevp != (struct list *)NULL; busdevp = busdevp->l_next) {
		if ((dev = finddev(busdevp->l_text)) == (struct device *)NULL) {
			if (DEBUG_LEVEL(8))
				printf("Add_bus:+\"%s\"\n",busdevp->l_text);
			/*
			 * a new bus device needs to be allocated.
			 */
			dev=(struct device *)perm_alloc(sizeof(struct device));
			dev_init(dev, busdevp->l_text);
			if (!device_names_initialized) {
				device_names_initialized = 1;
				device_names = create_symtab(DEVNM_HASHENTS);
			}
			if (insert_symtab(device_names, dev->d_name,
					 (opaque_ptr)dev) != (opaque_ptr)NULL) {
				fatal("internal error in add_bus()\n");
				exit(1);
			}
			dev->d_next = dtab;
			dtab = dev;
			if (DEBUG_LEVEL(12))
				printf ("New device: %s\n", dev->d_name);
			/*
			 * find connected device information
			 */
			dev->d_conn = findcon(dev->d_name);
			dev->d_maxunit = 0x7fffffff;
		} 
		*bl++ = dev;
		/*
		 * set type to BUS.
		 */
		dev->d_type |= T_BUS;
		/*
		 * Busp is the list of busses found so far.
		 */
		if (dev->d_busno == UNKNOWN) {
			dev->d_busno = Max_bus++;
			Busp[dev->d_busno] = dev;
			if (DEBUG_LEVEL(8)) {
				printf("Add_bus:\"%s\" is bus %d\n",dev->d_name,
							dev->d_busno);
			}
		}
		if (dev->d_bus_own != (struct device *)NULL) {
			fatal("bus %s already owned by %s",dev->d_name,
							dev->d_bus_own);
			exit(1);
		}
		dev->d_bus_own = cur;
	}
	cur->d_bus_own = cur;
	*bl = (struct device *)NULL;
	free_list(busdevs);
}


/*
 * maintain an array of block devices
 */

add_bdev(d) 
	register struct	device	*d;
{
	register int	block;

	block=d->d_block;
	if ((block > MAX_BDEV) || (block < 0)) {
		yyerror("number out of range (%d>%d)\n",block,MAX_BDEV);
		return;
	}
	if (Bdevp[block]) {
		yyerror("block device %d already defined\n",block);
		return;
	} else
		Bdevp[block] = d;
	if (block > Max_bdev)
		Max_bdev = block;
}


/*
 * maintain an array of character devices
 */

add_cdev(d) 
	register struct	device	*d;
{
	register int	c;

	c = d->d_character;
	if ((c > MAX_CDEV) || (c < 0)) {
		yyerror("number out of range (%d>%d)\n",c,MAX_CDEV);
		return;
	}
	if (Cdevp[c]) {
		yyerror("character device %d already defined by %s\n",
			c,Cdevp[c]->d_name);
		return;
	} else
		Cdevp[c] = d;
	if (c > Max_cdev)
		Max_cdev = c;
	if ((d->d_type & T_STREAMD) && (c > Max_cstr) )
		Max_cstr = c;
}


/*
 * maintain an array of filesystems
 * (Filesystem 0 is always not definned).
 */

add_fs(d) 
	struct	device	*d;
{
	register int	c;

	c = d->d_character;
	if (c == QUES)
		c = Max_fs++;
	if ((c > MAX_FS) || (c < 1)) {
		yyerror("number out of range (%d>%d)\n",c,MAX_FS);
		return;
	}
	if (Fsp[c]) {
		yyerror("filesystem %d already defined\n",c);
		return;
	} else
		Fsp[c] = d;
	if (++c > Max_fs)
		Max_fs = c;
}


/*
 * initialise a new connect structure.
 * (Note pseudo devices don't have an "on" device.)
 */

new_connect(hdc, archdef)
struct hdc *hdc;		/* Pointer to connect information */
boolean archdef;		/* TRUE if defining architecture; else FALSE */
{
	register struct connect *nc;
	struct connect *oc;

	/*  If tracing a new connection, the following is the old code....
	------------------------------------------------------------------
		if (hdc->type_spec){
			printf("%s ", hdc->dev_spec);
			if (hdc->dev_val >= 0)
				printf("%d ", hdc->dev_val);
			printf("on ");
			if (hdc->type_spec)
				printf("%s ", hdc->type_spec);
			if (hdc->type_val >= 0)
				printf("%s ", hdc->type_val);
			else if (hdc->type_val == QUES)
				printf("? ");
			printf("unit ");
			if (hdc->unit_spec >= 0)
				printf("%d ", hdc->unit_spec);
			else
				printf("? ");
		} else {
			printf("pseudo-device %s", hdc->dev_spec);
			if (hdc->dev_val >= 0)
				printf(" %d", hdc->dev_val);
		}
		printf("\n");
	}
	---------------------------------------------------------------*/
	if (archdef) {
		/*
		 *  If multiple "arch_sys <architecture>" directives occur
		 *  for the same architecture, then allow only one....
		 */
		if ((oc = findcon(hdc->dev_spec)) != (struct connect *)NULL)
			return;
	}
	nc = (struct connect *)perm_alloc(sizeof(struct connect));
	nc->c_d_name = hdc->dev_spec;
	nc->c_d_val = hdc->dev_val;
	nc->c_busnum = UNKNOWN;
	nc->c_t_name = hdc->type_spec;
	nc->c_t_val = hdc->type_val;
	nc->c_unit = hdc->unit_spec;
	nc->c_ddev = (struct device *)NULL;  /* filled in by check_connect() */
	nc->c_tdev = (struct device *)NULL;  /* filled in by check_connect() */
	nc->c_tail_d = (struct connect *)NULL;		/* '' */
	nc->c_on = (struct connect *)NULL;		/* '' */
	nc->c_next = (struct connect *)NULL;
	nc->c_next_d = (struct connect *)NULL;
	nc->c_nsegs = hdc->nsegs;
	nc->c_segsize = hdc->segsize;
	nc->c_segalign = hdc->segalign;
	hdc->nsegs = 0;
	hdc->segsize = 0;
	hdc->segalign = 1;

	/*
	 * search for other devices (different units?)
	 */
	if (archdef || (oc=findcon(hdc->dev_spec)) == (struct connect *)NULL) {
		if (DEBUG_LEVEL(12))
			printf("New connect %s\n",hdc->dev_spec);
		nc->c_numdev = 1;
		/*
		 * Chain on front of ctab
		 */
		if (ctab == (struct connect *)NULL)
			ctab = nc;
		else
			ctab_tail->c_next = nc;
		ctab_tail = nc;
	} else {
		/* chain on end */
		if (DEBUG_LEVEL(12))
			printf("Old connect %s\n",hdc->dev_spec);
		if (oc->c_next_d == (struct connect *)NULL)
			oc->c_next_d = nc;
		else
			oc->c_tail_d->c_next_d = nc;
		oc->c_tail_d = nc;
		oc->c_numdev++; 
		nc->c_numdev = oc->c_numdev;
	}	
}


/*
 * Look for the device (by name) in the list of
 * configured connections.
 */
struct connect *
findcon(dev)
	char	*dev;
{
	register struct	connect	*con;

	for(con=ctab; con!=(struct connect *)NULL; con=con->c_next) {
/*printf ("DEBUG: findcon: strcmp(%s, %s)\n", dev, con->c_d_name);*/
		if (strcmp(dev, con->c_d_name) == 0) 
			return(con);
	}
	return((struct connect *)NULL);
}

/*
 * Look for the device (by name and unit number) in the list of
 * configured connections.
 */
struct connect *
findncon(c)
	struct	connect	*c;
{
	register struct	connect	*con;
	register struct	connect	*c2;

	if (c->c_on != (struct connect *)NULL)
		return(c->c_on);
	con = findcon(c->c_t_name);
	if (c->c_t_val >= 0) {
		/* 
		 * find correct unit - spin down multiple devices
		 */
		for (c2=con; c2; c2=c2->c_next_d){
			if (c2->c_d_val == c->c_t_val) 
				return(c2);
		}
	}
	return (con);
}

/*
 * Look for the device (by name) in the list of configured hardware device. 
 * return a pointer to the structure or NULL.
 */
struct device *
finddev(devname)
register char *devname;
{
	register struct	device	*d;

	if (!device_names_initialized) {
		device_names_initialized = 1;
		device_names = create_symtab(DEVNM_HASHENTS);
	} else {
		d = (struct device *) search_symtab(device_names, devname);
		if (d != (struct device *)NULL) {
			if (DEBUG_LEVEL(12))
				printf("Finddev found \"%s\" \n", devname);
			return(d);
		}
	}
	/*
	 *  Check current device.
	 */
	if (cur != (struct device *)NULL) {
		if (strcmp(devname, cur->d_name) == 0) {
			if (DEBUG_LEVEL(12))
				printf("Finddev found \"%s\" \n", devname);
			return(cur);
		}
	}
	if (DEBUG_LEVEL(12))
		printf("Finddev cannot find \"%s\" \n", devname);
	return((struct device *)NULL);
}

/*
 * For each T_ON_BUS in dtab establish the bus connection.
 * for each file in ctab see if it has been configured
 * complete the ?dev fileds.
 */

int	Busses;

check_connect()
{
	static void implicit_default();
	static void check_on();
	register struct	connect	*con;
	register struct	connect	*subc;
	register struct device	*d;
	register struct	connect	*prevcon;
	char			buff[BUFSIZ];

	if (DEBUG_LEVEL(0))
		printf("\n\tChecking connections \n");

	/*
	 * first complete the device information for each driver or buss driver
	 */
	Busses = 0;
	prevcon = (struct connect *)NULL;
	for(con = ctab; con != (struct connect *)NULL; con = con->c_next) {
		if ( (d = finddev(con->c_d_name)) == (struct device *)NULL) {
			fflush(stdout);		/* In case stdout > stderr */
			fprintf(stderr,
		"ERROR: \"%s\" is not a supported device driver module\n",
				con->c_d_name);
			if (prevcon == (struct connect *)NULL)
				ctab = con->c_next;
			else
				prevcon->c_next = con->c_next;
			Trouble = TRUE;	/* Fail the configuration */
		} else {
			prevcon = con;
			con->c_ddev = d;
		}
	}
	/*
	 * now set the "connected on" information.
	 */
	for (con = ctab; con != (struct connect *)NULL; con = con->c_next) {
		d = con->c_ddev;
		if ((d->d_type&T_PSEUDO) && (con->c_t_name == (char *)NULL)) {
			/* 
			 *  Pseudo device.  Create the autoconfig
			 *  structures here since all information
			 *  is known.
			 */
			if (d->d_entry & (1L << L_INIT)) {
				(void)sprintf(buff,
					   "extern void	%s%s();\n",
					d->d_prefix,
					driver_entry[L_INIT].entry_name);
				perm_to_tail(&pseudo_x, buff);
				(void)sprintf(buff,
					   "{\t\"%s\", %d, %s_init},\n",
			                     con->c_d_name,con->c_d_val,
					     d->d_prefix);
			} else {
				(void)sprintf(buff,
					   "{\t\"%s\", %d, NULL },\n",
			                     con->c_d_name,con->c_d_val,
					     d->d_prefix);
			}
			/*
			 * defer the actual printing till later.
			 */
			perm_to_tail(&pseudos, buff);
			if ((d->d_maxunit>0) && (con->c_d_val>d->d_maxunit)) {
				fflush(stdout);	/* In case stdout > stderr */
				fprintf(stderr, "WARNING: more \"%s\" pseudos ",
					con->c_d_name);
				fprintf(stderr, "(%d) have been configured ",
					con->c_d_val);
				fprintf(stderr, "than are permitted (%d)\n",
					d->d_maxunit);
				Warning = TRUE;
			}
			if ((d->d_minunit>0) && (con->c_d_val<d->d_minunit)) {
				fflush(stdout);	/* In case stdout > stderr */
				fprintf(stderr, "WARNING: less \"%s\" pseudos ",
					con->c_d_name);
				fprintf(stderr, "(%d) have been configured ",
					con->c_d_val);
				fprintf(stderr, "than are permitted (%d)\n",
					d->d_minunit);
				Warning = TRUE;
			}
			d->d_numdev = con->c_d_val;
		} else {
			check_on(con, d);
			d->d_numdev = con->c_numdev;
			/*
			 * Now repeat as above for each multiple device if any.
			 */
			for (subc = con->c_next_d; subc; subc = subc->c_next_d)
				check_on(subc, d);
		}
	}
	/*
	 *  Define implicit defaults.
	 */
	if (Vers.version >= 107) {
		for (d = dtab; d != (struct device *)NULL; d = d->d_next) {
			if ((d->d_type & (T_PSEUDO|T_BLOCK|T_CHAR)) &&
			    d->d_numdev != UNKNOWN) {
				implicit_default(d);
			}
		}
	}
}

/*
 *  Implicitly default the number of devices parameter ("_xxxx_NUMDEV")
 *  for the given device.
 */
static void
implicit_default(d)
register struct device *d;		/* device */
{
	char buff[1024];
	register struct expr *ep;

	ep = new_expr();
	ep->ex_op = EXOPconst;
	ep->ex_u.constant = d->d_numdev;
	(void)sprintf(buff, "_%s_NUMDEV", d->d_uprefix);
	default_par(buff, FALSE, ep, (struct expr *)NULL,
		    (struct expr *)NULL, FALSE);
}

static void
check_on(con, dev)
	struct connect	*con;
	struct device	*dev;
{
	struct device	*d;

	if (con->c_ddev == (struct device *)NULL)
		con->c_ddev = dev;
	if ((d=finddev(con->c_t_name)) == (struct device *)NULL) {
		fflush(stdout);		/* In case stdout > stderr */
		fprintf(stderr,
		"ERROR: \"%s\" is not a supported bus or controller module\n",
					con->c_t_name);
		Trouble = TRUE;		/* Fail the configuration */
		return;
	}
	/*
	 * Set "on" linkage.
	 */
	con->c_tdev = d;
	con->c_on = findncon(con);

	/*
	 * If this is a buss type then add to list of bus types.
	 */
	if (con->c_ddev->d_type & T_BUS) {
		if (con->c_ddev->d_busno < 0) {
			fflush(stdout);		/* In case stdout > stderr */
			fprintf(stderr,
	"ERROR: buss \"%s\" has not been defined in any \"config\" file\n",
					d->d_name);
			Trouble = TRUE;	/* Fail the configuration */
			return;
		}
		if ((d->d_type & T_BUS) && (con->c_ddev != d)) {
			fflush(stdout);		/* In case stdout > stderr */
			fprintf(stderr,
			"ERROR: buss \"%s\" is connected to buss \"%s\"\n",
				d->d_name,con->c_t_name);
			Trouble = TRUE;	/* Fail the configuration */
			return;
		}

		con->c_busnum = Busses++;
		if (DEBUG_LEVEL(4)) {
			printf(
	"%s bus(busses[%d]) \"%s\"%d is on \"%s\"[buss#%d] %d unit %d\n",
				d->d_conn?"connected":"",
				con->c_busnum,
				con->c_d_name, 
				con->c_d_val,
				con->c_t_name,
				d->d_busno,
				con->c_t_val, con->c_unit);
		}
		/*
		 * add to tail of c_busses thread 
		 */
		con->c_busses = (struct connect *)NULL;
		if (busts == (struct connect *)NULL) {
			busts = con ;
		} else {
			busts_tail->c_busses = con;
		}
		busts_tail = con;
		return;
	} 
	if (DEBUG_LEVEL(4)) {
		printf("\"%s\"(\"%s\") %d is on \"%s\"(\"%s\")%d unit %d\n",
			con->c_d_name, con->c_ddev->d_prefix,
			con->c_d_val,
			con->c_t_name, con->c_tdev->d_prefix,
			con->c_t_val, con->c_unit);
	}
	if (d->d_type & T_BUS) {

		 /*
		  * add to tail of c_busses thread (bus drivers).
		  */
		con->c_busses = (struct connect *)NULL;
		if (busds == (struct connect *)NULL) {
			busds = con ;
		} else {
			busds_tail->c_busses = con;
		}
		busds_tail = con;
	}
	if (con->c_unit > dev->d_maxunit) {
		fflush(stdout);		/* In case stdout > stderr */
		fprintf(stderr,
   "WARNING: The unit number(%d) for \"%s\" has exceeded the maximum (%d)\n",
			con->c_unit,con->c_d_name,dev->d_maxunit);
		Warning = TRUE;
	}
}


/*
 * Process the connect structures generating the conf.c and config.h files
 * spin through list of all connects.
 */

do_conf()
{
	extern	void	print_config_params();	/* print configurable params */
	extern	struct	list	*conf_list;
	extern	struct	list	*confh_list;
	register FILE	*conf_fp;		/* conf.c file */
	register FILE	*config_fp;		/* config.h file */
	register struct	device	*d;
	char			conf_c[MAXPATH];
	char			config_h[MAXPATH];
	char			buff[BUFSIZ];

	(void)sprintf(conf_c, "%s/%s", Build_dir, CONF_C);
	(void)sprintf(config_h, "%s/%s", Build_dir, CONFIG_H);
	if (DEBUG_LEVEL(0))
		printf("Do_conf: %s\n",conf_c);

	SET_FILE(conf_c+Anchor_len+1);

	if ((conf_fp = fopen(conf_c, "w+")) == (FILE *)NULL) {
		fatal("cannot create: [%s]\n", conf_c);
		exit(1);
	}
	if ((config_fp = fopen(config_h, "w+")) == (FILE *)NULL) {
		fatal("cannot create: [%s]\n", config_h);
		exit(1);
	}

	(void)sprintf(buff,"struct fmodsw fmodsw[] = {\n");
	streams_module_count = 0;
	perm_to_tail(&modtab, buff);
	(void)sprintf(buff,"struct driver_module drivers[] = {\n");
	perm_to_tail(&driver_module, buff);
	(void)sprintf(buff,"void (*io_init[])() = {\n");
	perm_to_tail(&inits, buff);

	print_cat(conf_fp, conf_list, "\n");
	print_cat(config_fp, confh_list, "\n");

	for(d=dtab; d != (struct device *)NULL; d=d->d_next) {
		if (DEBUG_LEVEL(8))
			printf("Do_conf: %s type 0x%x\n", d->d_name, d->d_type);
		switch (d->d_type) {
		case T_BLOCK|T_CHAR:
		case T_PSEUDO|T_CHAR|T_BLOCK:
			bdev(conf_fp, d);
			cdev(conf_fp, d);
			break;
		case T_BLOCK:
			bdev(conf_fp, d);
			break;
		case T_PSEUDO:
		case T_PSEUDO|T_CHAR:
			cdev(conf_fp, d);
			break;
		case T_CHAR:
			cdev(conf_fp, d);
			break;
		case T_STREAMM:
			smod(conf_fp, d);
			driver_inits(d->d_entry, d->d_prefix);
			break;
		case T_STREAMD:
		case T_STREAMD|T_CHAR:
		case T_STREAMD|T_PSEUDO:
		case T_STREAMD|T_PSEUDO|T_CHAR:
			cdev(conf_fp, d);
			sdev(conf_fp, d);
			break;
		case T_BUSD|T_BUS:
			/* system buss only */
			bus(conf_fp, d);
			busd(conf_fp, d);
			break;
		case T_BUS:
			bus(conf_fp, d);
			break;
		case T_BUSD:
			busd(conf_fp, d);
			break;
		case T_FS:
			fs(conf_fp, d);
			break;
		case 0:
			/*
			 * Check if connected.
			 */
			if ( (d->d_on_bus != (struct device **)NULL) ||
			     (d->d_bus != (struct device **)NULL)      ) {
				fatal(
				"missing device specification for \"%s\"\n",
						d->d_name);
				exit(1);
			}
			continue;
		default:
			fatal(
			"illegal device specification for \"%s\" type 0x%x\n",
				d->d_name, d->d_type);
			exit(1);
		}
		if (DEBUG_LEVEL(8))
			printf("End of types\n");
		pr_extern(conf_fp, d->d_entry, d->d_prefix);
		pr_defines(config_fp, d);
	}
	if (DEBUG_LEVEL(8))
		printf("All connects proccesed\n");
	pr_bdev(conf_fp);
	pr_cdev(conf_fp);
	pr_fs(conf_fp);
	print_cat(conf_fp, modtab.l_head, "	{ NULL,NULL}\n};\n");
	fprintf(conf_fp, "int fmodcnt = %d;\n\n", streams_module_count);

	print_cat(conf_fp, inits.l_head, "\t0,\n};\n");

	print_cat(conf_fp, pseudo_x.l_head, "\n");
	fprintf(conf_fp,"struct pseudo_dev pseudo_dev[] = {\n");
	print_cat(conf_fp, pseudos.l_head, "{\t0 },\n};\n\n");

	pr_inits(conf_fp, init_list.l_head);
	pr_syscall(conf_fp);

	pr_bus(conf_fp, config_fp);
	print_cat(conf_fp, on_devices.l_head, "\n");
	print_cat(conf_fp, driver_module.l_head, "{\t0 },\n};\n\n");
	print_cat(conf_fp, busses.l_head, "\n");

	/*
	 * Write the configurable parameters section of the
	 * config.h file.
	 */
	print_config_params(config_fp);

	if (ferror(conf_fp) || fclose(conf_fp)) {
		unlink(conf_c);		/* prevent system build */
		fflush(stdout);
		fprintf(stderr, "Error writing file: [%s].\n", conf_c);
		Trouble = TRUE;
	}
	if (ferror(config_fp) || fclose(config_fp)) {
		unlink(config_h);	/* prevent system build */
		fflush(stdout);
		fprintf(stderr, "Error writing file: [%s].\n", config_h);
		Trouble = TRUE;
	}
	SET_FILE("");
}


/* 
 * block device 
 */
bdev(conf_fp, d) 
	register FILE *conf_fp;		/* conf.c file */
	register struct	device	*d;
{ 
	fprintf(conf_fp, "/* %s is a block device driver */\n", d->d_name);
	if (d->d_character != UNKNOWN)
		fprintf(conf_fp, "int	%s_b_major = %s_BLOCK;\n",
			d->d_prefix,d->d_uprefix);
}


/* 
 * character device (psuedo and stream) 
 */
cdev(conf_fp, d) 
	register FILE *conf_fp;		/* conf.c file */
	register struct	device	*d;
{ 
	if (DEBUG_LEVEL(8))
		printf("/* %s is a char device driver */\n", d->d_name);

	fprintf(conf_fp, "/* %s is a ", d->d_name);
	if (d->d_type & T_PSEUDO)
		fprintf(conf_fp, "pseudo ");
	if (d->d_type & T_STREAMD)
		fprintf(conf_fp, "stream ");
	fprintf(conf_fp, "char device driver */\n");
	if (d->d_character != UNKNOWN)
		fprintf(conf_fp, "int	%s_c_major = %s_CHARACTER;\n",
			d->d_prefix,d->d_uprefix);
}


/*
 * Streams device.
 */
sdev(conf_fp, d) 
	register FILE *conf_fp;		/* conf.c file */
	register struct	device	*d;
{ 
	fprintf(conf_fp, "/* %s is a streams driver */\n", d->d_name);
	fprintf(conf_fp, "extern	struct	streamtab	%sinfo;\n",
						d->d_prefix);
}


/*
 * Stream module
 */
smod(conf_fp, d) 
	register FILE *conf_fp;		/* conf.c file */
	register struct	device	*d;
{ 
	char	 		buff[BUFSIZ];
	register struct list	*listp;

	/*
	 *  Write the conf.c extern definitions for fmodsw table.
	 */
	fprintf(conf_fp, "/* %s is a streams module */\n", d->d_name);
	fprintf(conf_fp, "extern	struct streamtab	%sinfo;\n",
		d->d_prefix);
	listp = d->d_aliases.l_head;
	while (listp != NULL) {
		fprintf(conf_fp, "/* %s is a streams module alias for %s */\n",
			listp->l_text, d->d_name);
		fprintf(conf_fp,
			"extern	struct streamtab	%sinfo;\n",
			listp->l_text);
		listp = listp->l_next;
	}

	/*
	 *  Add to the conf.c fmodsw table initialization array.
	 */
	(void)sprintf(buff,"	{ \"%s\",&%sinfo },\n",
		      d->d_prefix, d->d_prefix);
	perm_to_tail(&modtab, buff);
	streams_module_count++;
	listp = d->d_aliases.l_head;
	while (listp != NULL) {
		(void)sprintf(buff,"	{ \"%s\",&%sinfo },\t/* %s: %s */\n",
			      listp->l_text, listp->l_text,
			      "streams module alias for", d->d_name);
		perm_to_tail(&modtab, buff);
		listp = listp->l_next;
		streams_module_count++;
	}
}


/*
 * filesystem
 */
fs(conf_fp, d) 
	register FILE *conf_fp;		/* conf.c file */
	register struct	device	*d;
{ 
	fprintf(conf_fp, "/* %s is a filesystem */\n", d->d_name);
}


/*
 * Bus type.
 */
bus(conf_fp, d) 
	register FILE *conf_fp;		/* conf.c file */
	register struct	device	*d;
{ 
	fprintf(conf_fp, "/* %s is a bus type */\n", d->d_name);
}

/*
 * Bus device driver.
 */
busd(conf_fp, d) 
	register FILE *conf_fp;		/* conf.c file */
	register struct	device	*d;
{ 
	fprintf(conf_fp, "/* %s is a bus drivers*/\n", d->d_name);
}


/*
 *  Print the extern definition for the specified options
 */
pr_extern(conf_fp, entry_set, prefix)
	register FILE *conf_fp;		/* conf.c file */
	register long entry_set;
	char *prefix;
{
	register int entry_num;
	register long entry_mask;

	if (DEBUG_LEVEL(12))
		printf("Pr_extern \"%s\"\n",prefix);
	
	entry_mask = 1;
	entry_num = 0;
	while (entry_set) {
		if (entry_set & entry_mask) {
			fprintf(conf_fp, "extern %s %s%s();\n",
					driver_entry[entry_num].entry_type,
					prefix,
					driver_entry[entry_num].entry_name);
		}
		entry_set &= ~entry_mask;
		entry_mask <<= 1;
		entry_num++;
	}
}


/*
 *  Print the name for a device driver entry point.
 */
append_entry(buff, d, entry_point)
	char *buff;
	register struct device *d;
	register int entry_point;
{
	char localbuffer[BUFSIZ];

	if (d->d_entry & (1L << entry_point)) {
		(void)sprintf (localbuffer, "%s%s, ", d->d_prefix,
				driver_entry[entry_point].entry_name);
	} else {
		(void)sprintf (localbuffer, "%s, ",
			       driver_entry[entry_point].entry_default);
	}
	strcat(buff, localbuffer);
}


/*
 * print the extern definition for init routines
 */
pr_inits(conf_fp, init_funcs)
	register FILE *conf_fp;		/* conf.c file */
	struct list *init_funcs;
{
	register struct	list	*ifunc;

	if (DEBUG_LEVEL(12))
		printf("Pr_inits \n");
	for (ifunc = init_funcs;
	     ifunc != (struct list *)NULL; ifunc = ifunc->l_next) {
		fprintf(conf_fp, "extern void %s();\n", ifunc->l_text);
	}
	fprintf(conf_fp, "void	(*init_tab[])() = {\n");
	for (ifunc = init_funcs;
	     ifunc != (struct list *)NULL; ifunc = ifunc->l_next) {
		fprintf(conf_fp, "	%s,\n", ifunc->l_text);
	}
	fprintf(conf_fp, "	0,\n};\n\n");
}



/*
 * complete the bdevsw table
 * (defined in sys/conf.h)
 *	struct bdevsw {
 *		int	(*d_open)();
 *		int	(*d_close)();
 *		int	(*d_strategy)();
 *		int	(*d_miniphys)();
 *		int	(*d_psize)();
 *		int	(*d_print)();
 *	};
 */

pr_bdev(conf_fp)
	register FILE *conf_fp;		/* conf.c file */
{
	register struct	device	*d;
	static	 char	format[] = " { %10s,%11s,%15s,%14s,%10s,%10s },\n";
	static	 char	nd[] = "nodev";
	register int	i;

	fprintf(conf_fp, "int	bdevcnt = %d;\n",Max_bdev+1);
}


/*
 * complete the cdevsw table
 * (defined in sys/conf.h)
 *	struct cdevsw {
 *		int	(*d_open)();
 *		int	(*d_close)();
 *		int	(*d_read)();
 *		int	(*d_write)();
 *		int	(*d_ioctl)();
 *		int	(*d_mmap)();
 *		struct tty *d_ttys;
 *		struct streamtab *d_str;
 *	};
 */

pr_cdev(conf_fp)
	register FILE *conf_fp;		/* conf.c file */
{
	char		buff[BUFSIZ];
	static	char
		*format = " { %10s,%10s,%10s,%10s,%10s,%10s,%10s,%10s%s%s },\n";
	static	char	nd[] = "nodev";
	static	char	nl[] = "nulldev";
	register struct	device	*d;
	register int	i;

	fprintf(conf_fp, "#define T_NULL	(struct tty*)NULL\n");
	fprintf(conf_fp, "#define S_NULL	(struct streamtab *)NULL\n");
	fprintf(conf_fp, "int cdevcnt = %d;\n",Max_cdev+1);
	fprintf(conf_fp, "int cstrcnt = %d;\n",Max_cstr+1);
	for(i=0; i<=Max_cdev; i++) {
		if ((d = Cdevp[i]) == (struct device *)NULL) {
			(void)sprintf(buff,"/* %2.2d */ S_NULL,\n",
								i,d->d_prefix);
		} else {
			if (d->d_type & T_STREAMD) {
				(void)sprintf(buff,"/* %2.2d */ &%sinfo,\n",
								i,d->d_prefix);
			} else {
				(void)sprintf(buff,"/* %2.2d */ S_NULL,\n",
								i,d->d_prefix);
			}
		}
		if (i <= Max_cstr)
			perm_to_tail(&cstreams, buff);
	}
	fprintf(conf_fp, "struct	streamtab	*cstrtab[] = {\n");
	print_cat(conf_fp, cstreams.l_head, "\n};\n");
}


pr_syscall(conf_fp)
	register FILE *conf_fp;		/* conf.c file */
{
	int	i;

	if (Max_syscall == 0)
		return;
	fprintf(conf_fp,"\n");
	fprintf(conf_fp,"extern nosyscall();\n");
	fprintf(conf_fp,"extern syscall();\n");
	for (i=0; i <= Max_syscall; i++){
		if (Syscalls[i]) {
			fprintf(conf_fp,"extern char *syscall%dnames[];\n",i);
			fprintf(conf_fp,"extern int nsysent%d;\n",i);
			fprintf(conf_fp,"extern struct sysent sysent%d[];\n",i);
		}
	}

	fprintf(conf_fp,"struct syscallhandler syscall_handler[] = {\n");
	for (i=0; i <= Max_syscall; i++){
		if (Syscalls[i]) {
			fprintf(conf_fp,
			      "\t{syscall,sysent%d,&nsysent%d,syscall%dnames},",
				i ,i ,i);
		} else {
			fprintf(conf_fp,"\t{nosyscall, 0, 0, 0},");
		}
		fprintf(conf_fp,"\t /* [%d] */\n", i);
	}
	fprintf(conf_fp,"};\nint syscall_nhandler = %d;\n\n", Max_syscall+1);
}

pr_bus(conf_fp, config_fp)
	register FILE *conf_fp;		/* conf.c file */
	register FILE *config_fp;	/* config.h file */
{
	register struct	device	*d;
	register struct	connect	*con;
	register struct	connect	*c;
	register int		i;
	register int		n;
	register int		ifs;
	char			buff[BUFSIZ];
	char			name[BUFSIZ];
	char			*prf;

	(void)sprintf(buff,"struct bus busses[] = {\n");
	perm_to_tail(&busses, buff);

	/*
	 * scan the device table.
	 * Assist autoconfig by generating gen_devices[] tables.
	 */
	n = 0;
	for(con=ctab; con; con=con->c_next) {
		d = con->c_ddev;
		prf = d->d_prefix;
		/*
		 * don't look at pseudo devices or busses.
		 */
		if ((con->c_t_name == (char *)NULL) || (d->d_type&T_BUS))
			continue;
		/*
		 * dont look at malformed connections or the
		 * the system bus (connected to itself.
		 */
		if ((con->c_on == (struct connect *)NULL) || (d==con->c_tdev))
			continue;

		(void)sprintf(name,"\"%s\"", prf);
		(void)sprintf(buff,"{%10s, %3d, 0, ", name, con->c_numdev);
		append_entry(buff, d, L_PROBE);
		append_entry(buff, d, L_INIT);
		append_entry(buff, d, L_INTR);
		append_entry(buff, d, L_POLL);
		strcat(buff, "\n\t ");
		perm_to_tail(&driver_module, buff);
		(void)sprintf(buff, "%s_on_busses,", prf );
		perm_to_tail(&driver_module, buff);
		(void)sprintf(buff, "(struct gen_device *)%s_devices,\n",
								prf);
		perm_to_tail(&driver_module, buff);
		(void)sprintf(buff, 
			 "\t sizeof (struct %s_device),},\n", prf);
		perm_to_tail(&driver_module, buff);
		/*
		 * If this device is connected to a bus then define
		 * the xxx_device struct for it.
		 */
		(void)sprintf(buff, "#include \"sys/%s_device.h\"\n", prf);
		perm_to_tail(&on_devices, buff);
		(void)sprintf(buff, "struct %s_device %s_devices[] = {\n",
						prf, prf);
		perm_to_tail(&on_devices, buff);
		/* 
		 * for each device of this type form a table of connections.
		 */
		for (c=con; c; c=c->c_next_d) {
			if ((c->c_t_val < 0) || (c->c_on->c_busnum == 0)) {
				(void)sprintf(buff, "\t{-%s,", 
						c->c_tdev->d_uprefix);
			} else {
				(void)sprintf(buff, "\t{%d,", 
					c->c_on->c_busnum);
			}
			perm_to_tail(&on_devices, buff);
			if (c->c_unit >= 0) 
				(void)sprintf(buff, "%d, ", c->c_unit);
			else
				(void)sprintf(buff, "-1, ");
			perm_to_tail(&on_devices, buff);
			(void)sprintf(buff,"%d, %d, %d, ",
					c->c_segsize, c->c_segalign, 
					c->c_nsegs);
			perm_to_tail(&on_devices, buff);
			(void)sprintf(buff,"&drivers[%d]", n);
			perm_to_tail(&on_devices, buff);
			(void)sprintf(buff,"},\n");
			perm_to_tail(&on_devices, buff);
		}
		(void)sprintf(buff, "};\n");
		perm_to_tail(&on_devices, buff);
		n++;
	}
	/*
	 * scan the bus type table.
	 */
	for (con=busts; con; con=con->c_busses) {
		d = con->c_ddev;
		if (d == con->c_tdev) {
			sprintf(buff,"/* %d */{ %s, },\n",
				con->c_busnum, d->d_uprefix);
			perm_to_tail(&busses, buff);
			continue;
		} 
		prf = con->c_tdev->d_prefix;
		(void)sprintf(buff, 
		 	"/* %d */ { %s, (struct gen_device *)&%s_devices[%d],",
			 con->c_busnum,
		         d->d_uprefix, 
			 prf,con->c_t_val);
		perm_to_tail(&busses, buff);
		for (ifs=0; ifs<nif; ifs++) {
			(void)sprintf(buff, "\n\t%s_%s_%s, ",
			    prf,d->d_prefix, biface[ifs]);
			perm_to_tail(&busses, buff);
		}
		sprintf(buff, "},\n");
		perm_to_tail(&busses, buff);
	}
	fprintf(conf_fp, "char *bus_type[] = {\n\t\"nobus\",\n");
	for (i=1; i<Max_bus; i++) {
		d = Busp[i];
		fprintf(conf_fp, "\t\"%s\",\n", d->d_name);
		/*
		 * Add to header files.
		 */
		fprintf(config_fp,"#define %-20s\t%d\n",d->d_uprefix,i);
	}
	fprintf(conf_fp, "\t(char *)NULL,\n};\n\n");

	sprintf(buff, "{ NOBUS },\n};\n\n");
	perm_to_tail(&busses, buff);
}


/*
 * complete the filesystem table
 * struct vfsops *vfssw[];
 */

pr_fs(conf_fp)
	register FILE *conf_fp;		/* conf.c file */
{
	register int	i;

	fprintf(conf_fp,"/* filesystem table */\n");
	for (i=1; i<Max_fs; i++) {
		if (Fsp[i])
			fprintf(conf_fp,"extern	struct	vfsops	%s_vfsops;\n",
						Fsp[i]->d_prefix);
	}
	fprintf(conf_fp,"\nstruct vfsops *vfssw[] = {\n");
	fprintf(conf_fp,
		"(struct vfsops *)0,	/* 0 is always 0 - no filesystem */\n");
	for (i=1; i<Max_fs; i++) {
		if (Fsp[i]) {
			fprintf(conf_fp,"&%s_vfsops,		/* %d */\n",
				Fsp[i]->d_prefix,i);
		} else {
			fprintf(conf_fp,
				"(struct vfsops *)0,\t/* %d no filesystem */\n",
				i);
		}
	}
	fprintf(conf_fp, "};\n\n");
	fprintf(conf_fp, "int	mount_ntypes = %d;\n\n",Max_fs);
}


/*
 * Add manifest constants the the config.h file.
 */
pr_defines(config_fp, d)
	register FILE *config_fp;		/* conf.c file */
	register struct device	*d;
{
	char buff[1024];

	if (d->d_block != UNKNOWN) {
		sprintf(buff, "%s_BLOCK", d->d_uprefix);
		fprintf(config_fp, "#define %-20s\t%d\n", buff, d->d_block);
	}
	if (d->d_character != UNKNOWN) {
		sprintf(buff, "%s_CHARACTER", d->d_uprefix);
		fprintf(config_fp, "#define %-20s\t%d\n", buff, d->d_character);
	}
	/*
	 *  Pseudos can be fake devices (has everything except major/minor).
	 */
	if ((d->d_type & (T_PSEUDO|T_BLOCK|T_CHAR)) && d->d_numdev != UNKNOWN) {
		if (Vers.version <= 106) {
			(void)sprintf(buff, "%s_NUMDEV", d->d_uprefix);
			fprintf(config_fp,"#define %-20s\t%d\n",
				buff, d->d_numdev);
		}
	}
}


/*
 * If an init routine is specified add to the init table.
 */
driver_inits(entry_set, prefix)
	register long entry_set;
	char *prefix;
{
	char	buff[BUFSIZ];

	if (entry_set & (1L << L_INIT)) {
		(void)sprintf(buff, "\t%s%s,\n", prefix,
			      driver_entry[L_INIT].entry_name);
		perm_to_tail(&inits, buff);
	}
}

/*
 * maintain an array of system call table entries
 */

add_syscall(tab)
	int	tab;
{
	if (DEBUG_LEVEL(0))
		printf("Adding system call table #%d\n" ,tab);
	if ((tab > MAX_SYSCALL) || (tab < 0)) {
		yyerror("number out of range (%d>%d)\n" ,tab, MAX_SYSCALL);
		return;
	}
	if (Syscalls[tab]) {
		yyerror("System call table %d already defined\n", tab);
		return;
	} else
		Syscalls[tab] = 1;
	if (tab > Max_syscall)
		Max_syscall = tab;
}
