static char sccsid[] = "%W% %Y% %Q% %G%";
static char rcsid[] = "$Header: xd_utils.c,v 800.1 85/10/21 16:22:38 root Exp $";
#include "ex.h"			/* Configuration constants */
#include "xm.h"			/* Defines download driver */
#include "xd.h"			/* Defines (TCI/DECnet) logical link driver */

#include	"../h/types.h"		/* Commonly used data types */

#include	"../machine/pte.h"	/* VM page table definitions */
#include	"../h/param.h"		/* Process, memory and file sys parameters */
#include	"../h/systm.h"		/* Kernel global variables */
#include	"../h/mbuf.h"		/* Net/Comm buffer definitions */
#include	"../h/socket.h"	/* Socket interface definitions */
#include	"../h/vmmac.h"		/* Virtual memory address conversion macros */
#include	"../h/map.h"		/* Resource allocation maps */
#include	"../h/ioctl.h"		/* System wide IOCTL definitions */
#include	"../h/dir.h"		/* File directory definitions */
#include	"../h/buf.h"
#include	"../h/inode.h"
#include	"../h/uio.h"
#include	"../h/user.h"		/* U Page definitions (per user blocks) */
				/*  ../h/user.h includes 
					../machine/pch.h
					../h/dmap.h
					../h/time.h
					../h/resource.h
					../h/errno.h
				 */
#include	"../h/proc.h"
#include	"../h/kernel.h"	/* Kernel variables */
#include	"../h/nami.h"	/* Used to call namei() */
#include "../net/if.h"		/* Network interface definitions */
#include "../netinet/if_ether.h"	/* Standard Ethernet definitions */
#include "../s32dev/mbvar.h"	/* Multibus definitions (autoconfig) */
#ifdef VALIDnet
#include "../vnet/vnet.h"	/* Valid Network definitions */
#include "../conn/conn.h"	/* Valid connection management */
#include "../rpc/rpc.h"		/* Remote procedure call definitions */
#endif VALIDnet

#include	"../s32dev/exreg.h"	/* EXOS 201 definitions */
#include	"../s32dev/exdef.h"	/* Standard EXOS definitions */
#include 	"../s32dev/exioctl.h"	/* Definition of ioctl's */
#include	"../tci/tci.h"		/* TCI standard definitions */
#include	"../tci/exos_decnet.h"	/* Decnet Exos interface */
#include	"../tci/community.h"	/* TCI/Community User Definitions */
#include	"../tci/exos_sync.h"	/* Sync/Kludge buffer defines */

#define	BSIZE CLBYTES

extern struct ex_softc EX_SOFTC(NEX);	/* Controller/driver state and data */
extern struct mb_device *EXINFO(NEX);	/* Multibus device descriptor */
extern ExosSync exos_sync[];		/* Kludge (sync/reply) pool */

long xd_debug = 0;


/*
 * ll_findmsg:
 *      - first find a sync. buffer to hold result of message
 *      - find next available message on "host to exos" message queue
 *      - sleep if necessary, to get one
 *	- do not call at interrupt level
 */
ExosPacket *
ll_findmsg( xs, command )
struct ex_softc *xs;
u_char command;
	{
	register ExosPacket *mp;
	register ExosSync *xs_p;
	register int s;

	/* go to network priority */
	s = splnet();

	while( 1 )
		{

		/* Allocate a sync. buffer; mark it as used */
		/* If none, sleep waiting for one */
		for( xs_p = exos_sync; xs_p < &exos_sync[NUM_EXOS_SYNC]; xs_p++ )
			if( xs_p->xsy_state == XSY_FREE )
				{
				xs_p->xsy_state = XSY_BUSY;
				goto findmsg;
				}

		printf( "findmsg(): no free sync. buffers, sleep!\n" );
		goto nightynight;

findmsg:
		if( mp = (ExosPacket *)exGetCbuf( xs, HSAP_DLL, EXSAP_DLL ) )
			{
			xs_p->xsy_baddr = 0;
			mp->ex_recp = (u_long)xs_p;
			splx( s );
			DPRINT( xd_debug, 1, ( "ll_findmsg:found(%x,%x)\n", mp, xs_p ) );

			/*
			 * The length is the entire ExosPacket structure
			 * header starting at the length field...
			 */
			mp->ex_length = sizeof(ExosPacket) - 8;

			mp->ex_exsid = EXSAP_TCI;
			mp->ex_hsid = HSAP_TCI;
			mp->ex_cmd_type = command;
			mp->ex_bd.eb_len = 0;
			mp->ex_bd.eb_disc = BFD_USER;
			mp->ex_nids = 0;
			mp->ex_bds = 1;
			mp->ex_resp = 0;
			return( mp );
			}

		printf( "ll_findmsg: no free queue buffers, sleep!\n" );
		xs_p->xsy_state = XSY_FREE;         /* release sync. buf */

nightynight:

		/*
		 * Flag that a process is waiting for either a circular queue buffer
		 * or a sync. (sync/reply) buffer.
		 */

		/* !!! GOING TO SLEEP AT splnet !!! */
		xs->flags |= EX_WAITING;
		sleep( (caddr_t)&xs->flags, PZERO + 1 );
		xs->flags &= ~EX_WAITING;
		}
	}







/*
 * ll_send:
 *      - send a network message via the message queue's to the exos
 *      - bump board to tell it to go
 *      - wait for operation to complete and return the pointer to the
 *        result info (kept in sync.)
 *      - must not be called at interrupt level
 */
ExosSync *
ll_send( xs, dev, mp, dontint, do_sleep )
register struct ex_softc *xs;
register dev_t dev;
register ExosPacket *mp;
int dontint;
int do_sleep;
	{
	register ExosSync *xs_p;
	register int s;
	label_t lqsav;
	struct exdevice * exaddr;

	xs_p = (ExosSync*)mp->ex_recp;
	exaddr = (struct exdevice *)( EXINFO( minor( dev ) )->md_addr );

	DPRINT( xd_debug, 1, ( "ll_send: req(%x, %x, %x, %x, %x)\n",
		xs, dev, mp, xs_p, dontint ) );

	if( !xs->flags & EX_TCInet )
		{
		printf( "ll_send: not TCI\n" );
		u.u_error = ENXIO;
		return;
		}

	/* change to network interrupt level */
	s = splnet();

	/*
	 * If this proc gets hit by a signal (possibly from arrival of
	 * out-of-band data), free exos resources, THEN do the non-local goto
	 * to exit current system call.  This leaves a message pending
	 * on the board, but since we here clear only XSY_BUSY, no one
	 * else will try to use this sync. buffer until the pending
	 * message comes back from the board and the int. routine clears
	 * XSY_WAITING.
	 */
	lqsav = u.u_qsave;
	if( setjmp( &u.u_qsave ) )
		{
		if( xs->flags & EX_WAITING )
			wakeup( (caddr_t)&xs->flags );
		xs_p->xsy_state &= ~XSY_BUSY;
		splx( s );

		DPRINT( xd_debug, 1, ( "ll_send: resuming(%x, %x)\n",
			u.u_procp->p_addr, lqsav ) );

		longjmp( &lqsav );
		}
	else	{
		xs_p->xsy_dev = dev;
		if( do_sleep )
			xs_p->xsy_state |= XSY_WAITING;
		xs_p->xsy_proc = u.u_procp;
		mp->ex_status |= MH_EXOS;
		exaddr->portb = 0;

		while( xs_p->xsy_state & XSY_WAITING )
			sleep( (caddr_t)xs_p, PZERO + 1 ); /* interruptable */
		}

	u.u_qsave = lqsav;

	/* go back to normal priority */
	splx( s );

	DPRINT( xd_debug, 1, ( "ll_send: result(%x)\n", xs_p->xsy_reply ) );

	return( xs_p );
	}





/*
 *	free_sync_buffer()
 *
 *	Wakeup all processes waiting for a synchronization buffer
 */
free_sync_buffer( sync_ptr, exos_soft_struct )
register ExosSync *sync_ptr;
register struct ex_softc *exos_soft_struct;
	{

	sync_ptr->xsy_state = XSY_FREE;
	if( exos_soft_struct->flags & EX_WAITING )
		wakeup( (caddr_t)&exos_soft_struct->flags );
	}



char *xd_name;

xd_name_func()
	{

	return( *xd_name++ );
	}

xd_reopen( old_name, new_name )
char *old_name;
char *new_name;
	{
	register int i;
	register struct file *fp;
	register struct inode *ip;
	register struct inode *ip_old;
	register struct inode *ip_new;

	DPRINT( xd_debug, 0, ( "xd_reopen( %s, %s )\n", old_name, new_name ) );

	/* find the inode of the original device */
	xd_name = old_name;
	if( ( ip_old = namei( xd_name_func, LOOKUP, 0 ) ) == NULL )
		{
		DPRINT( xd_debug, 0, ( "Couldn't nami %s\n", old_name ) );
		return( FAIL );
		}

	/* unlock the inode and release it once (the open) */
	iunlock( ip_old );
	irele( ip_old );

	/* look through the user's file table for this device's inode */
	for( i = 0; i < NOFILE; ++i )
		{
		if( ( fp = u.u_ofile[i] ) == NULL )
			continue;
		if( ( ip = (struct inode *)fp->f_data ) == ip_old )
			break;
		}

	/* did we find it? */
	DPRINT( xd_debug, 0, ( "xd_reopen, i=%d, ip=%x, fp=%x\n", i, ip, fp ) );
	if( i == NOFILE )
		{
		DPRINT( xd_debug, 0, ( "not found\n" ) );
		return( FAIL );
		}

	/* get a new inode pointing to the unused new device */
	xd_name = new_name;
	ip_new = namei( xd_name_func, LOOKUP, 0 );
	DPRINT( xd_debug, 0, ( "new inode pointer = %x\n", ip_new ) );
	if( ip_new == NULL )
		{
		DPRINT( xd_debug, 0, ( "couldn't allocate it\n" ) );
		return( FAIL );
		}

	/* release the inode for the time it was gotten in the namei above */
	irele( ip_old );

	/* make the file pointer point to the new inode */
	fp->f_data = (caddr_t)ip_new;

	/* unlock the new inode so that when we close everything will be kosher */
	iunlock( ip_new );

	return( SUCCESS );
	}

