/*
 *
 * Copyright 1998-1999, University of Notre Dame.
 * Authors: Jeffrey M. Squyres, Kinis L. Meyer with M. D. McNally 
 *          and Andrew Lumsdaine
 *
 * This file is part of the Notre Dame LAM implementation of MPI.
 *
 * You should have received a copy of the License Agreement for the
 * Notre Dame LAM implementation of MPI along with the software; see
 * the file LICENSE.  If not, contact Office of Research, University
 * of Notre Dame, Notre Dame, IN 46556.
 *
 * Permission to modify the code and to distribute modified code is
 * granted, provided the text of this NOTICE is retained, a notice that
 * the code was modified is included with the above COPYRIGHT NOTICE and
 * with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
 * file is distributed with the modified code.
 *
 * LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
 * By way of example, but not limitation, Licensor MAKES NO
 * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
 * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
 * OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
 * OR OTHER RIGHTS.  
 *
 * Additional copyrights may follow.
 *
 *	Ohio Trollius
 *	Copyright 1995 The Ohio State University
 *	GDB/RBD
 *
 *	$Id: couter.c,v 6.3 1999/06/12 18:04:34 kmeyer1 Exp $
 *
 *	Function:	- client protocols
 *			- OTB version
 */

#include <lam_config.h>

#include <errno.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>

#include <kio.h>
#include <kreq.h>
#include <terror.h>
#include <typical.h>

/*
 * local functions
 */
static void		cipc_preempt();

/*
 * external functions
 */
extern int		_cio_kreq();		/* kernel request/reply */
extern int		_cio_kreqfront();	/* kernel request */
extern int		_cio_kreqback();	/* kernel reply */
extern int		_cio_init();		/* init kernel interface */
extern int		_cio_send();		/* transfer kernel msg */
extern int		_cio_recv();		/* transfer kernel msg */
extern void		_cio_cleanup();		/* frees resources */
extern void		_ksig_follow();		/* check signals */
extern void		(*_lam_signal())();	/* portable signal() */

/*
 * external variables
 */
extern struct kio_t	_kio;			/* Kernel I/O */

/*
 *	_cipc_init
 *
 *	Function:	- initializes OTB client interface
 *	Returns:	- 0 or ERROR
 */
int
_cipc_init()

{
	if (_cio_init()) return(LAMERROR);
/*
 * Vector LAM_SIGUSR to preemption routine.
 */
	if (_lam_signal(LAM_SIGUSR, cipc_preempt) == SIG_ERR) return(LAMERROR);

	return(0);
}

/*
 *	_cipc_ksend
 *
 *	Function:	- OTB kernel message send
 *			- makes KQSEND request to kernel
 *	Accepts:	- kernel request ptr
 *			- kernel reply ptr
 *	Returns:	- 0 or ERROR
 */
int
_cipc_ksend(pkq, pkr)

struct kreq		*pkq;
struct kreply		*pkr;

{
	int		r;
	sigset_t	sigs_preempt;

	if (_kio.ki_pid != getpid()) {
		errno = ENOTATTACHED;
		return(LAMERROR);
	}

	memcpy((char *) pkq->kq_fyi, (char *) _kio.ki_fyi,
			sizeof(_kio.ki_fyi));

	sigemptyset(&sigs_preempt);
	sigaddset(&sigs_preempt, LAM_SIGUSR);
	sigprocmask(SIG_BLOCK, &sigs_preempt, (sigset_t *) 0);

	if (_cio_kreq(pkq, pkr)) {
		sigprocmask(SIG_UNBLOCK, &sigs_preempt, (sigset_t *) 0);
		return(LAMERROR);
	}

	if (pkr->kr_reply) {
		sigprocmask(SIG_UNBLOCK, &sigs_preempt, (sigset_t *) 0);
		return(0);
	}

	pkq->kq_msg.k_length = pkr->kr_length;
	r = _cio_send(&pkq->kq_msg);
	sigprocmask(SIG_UNBLOCK, &sigs_preempt, (sigset_t *) 0);

	return(r);
}

/*
 *	_cipc_krecvfront
 *
 *	Function:	- OTB kernel message receive
 *			- makes KQRECV request to kernel
 *	Accepts:	- kernel request ptr
 *	Returns:	- client/kernel comm. handle or ERROR
 */
int
_cipc_krecvfront(pkq)

struct kreq		*pkq;

{
	sigset_t	sigs_preempt;
	int		r;

	if (_kio.ki_pid != getpid()) {
		errno = ENOTATTACHED;
		return(LAMERROR);
	}

	memcpy((char *) pkq->kq_fyi, (char *) _kio.ki_fyi,
			sizeof(_kio.ki_fyi));

	sigemptyset(&sigs_preempt);
	sigaddset(&sigs_preempt, LAM_SIGUSR);
	sigprocmask(SIG_BLOCK, &sigs_preempt, (sigset_t *) 0);

	if ((r = _cio_kreqfront(pkq)) < 0) {
		sigprocmask(SIG_UNBLOCK, &sigs_preempt, (sigset_t *) 0);
		return(LAMERROR);
	}

	return(r);
}

/*
 *	_cipc_krecvback
 *
 *	Function:	- OTB kernel message receive
 *			- receives reply for KQRECV from kernel
 *			- transfers message
 *	Accepts:	- kernel request ptr
 *			- kernel reply ptr
 *	Returns:	- 0 or ERROR
 */
int
_cipc_krecvback(pkq, pkr)

struct kreq		*pkq;
struct kreply		*pkr;

{
	int		r;
	sigset_t	sigs_preempt;

	sigemptyset(&sigs_preempt);
	sigaddset(&sigs_preempt, LAM_SIGUSR);

	if (_cio_kreqback(pkr)) {
		sigprocmask(SIG_UNBLOCK, &sigs_preempt, (sigset_t *) 0);
		return(LAMERROR);
	}

	if (pkr->kr_reply) {
		sigprocmask(SIG_UNBLOCK, &sigs_preempt, (sigset_t *) 0);
		return(0);
	}

	pkq->kq_msg.k_length = pkr->kr_length;
	r = _cio_recv(&pkq->kq_msg);
	sigprocmask(SIG_UNBLOCK, &sigs_preempt, (sigset_t *) 0);

	return(r);
}

/*
 *	_cipc_ksrfront
 *
 *	Function:	- OTB kernel message send/receive front end
 *			- makes KQSEND followed by KQRECV request to kernel
 *	Accepts:	- kernel request ptr
 *			- kernel reply ptr
 *	Returns:	- 0 or client/kernel socket
 */
int
_cipc_ksrfront(pkq, pkr)

struct kreq		*pkq;
struct kreply		*pkr;

{
	sigset_t	sigs_preempt;
	int		r;

	if (_kio.ki_pid != getpid()) {
		errno = ENOTATTACHED;
		return(LAMERROR);
	}

	memcpy((char *) pkq->kq_fyi, (char *) _kio.ki_fyi,
			sizeof(_kio.ki_fyi));

	sigemptyset(&sigs_preempt);
	sigaddset(&sigs_preempt, LAM_SIGUSR);
	sigprocmask(SIG_BLOCK, &sigs_preempt, (sigset_t *) 0);

	if ((r = _cio_kreqfront(pkq)) < 0) {
		sigprocmask(SIG_UNBLOCK, &sigs_preempt, (sigset_t *) 0);
		return(LAMERROR);
	}

	if (_cio_kreqback(pkr) < 0) {
		sigprocmask(SIG_UNBLOCK, &sigs_preempt, (sigset_t *) 0);
		return(LAMERROR);
	}

	if (pkr->kr_reply) {
		sigprocmask(SIG_UNBLOCK, &sigs_preempt, (sigset_t *) 0);
		return(0);
	}

	pkq->kq_msg.k_length = pkr->kr_length;

	if (_cio_send(&pkq->kq_msg)) {
		sigprocmask(SIG_UNBLOCK, &sigs_preempt, (sigset_t *) 0);
		return(LAMERROR);
	}

	return(r);
}

/*
 *	_cipc_ksrback
 *
 *	Function:	- OTB kernel message send/receive back end
 */
int
_cipc_ksrback(pkq, pkr)

struct kreq		*pkq;
struct kreply		*pkr;

{
	int		r;
	sigset_t	sigs_preempt;

	sigemptyset(&sigs_preempt);
	sigaddset(&sigs_preempt, LAM_SIGUSR);

	if (_cio_kreqback(pkr)) {
		sigprocmask(SIG_UNBLOCK, &sigs_preempt, (sigset_t *) 0);
		return(LAMERROR);
	}

	if (pkr->kr_reply) {
		sigprocmask(SIG_UNBLOCK, &sigs_preempt, (sigset_t *) 0);
		return(0);
	}

	pkq->kq_msg2.k_length = pkr->kr_length;

	r = _cio_recv(&pkq->kq_msg2);
	sigprocmask(SIG_UNBLOCK, &sigs_preempt, (sigset_t *) 0);

	return(r);
}

/*
 *	_cipc_kreq
 *
 *	Function:	- OTB kernel request
 *			- makes request to kernel
 *	Accepts:	- kernel request ptr
 *			- kernel reply ptr
 *	Returns:	- 0 or ERROR
 */
int
_cipc_kreq(pkq, pkr)

struct kreq		*pkq;
struct kreply		*pkr;

{
	sigset_t	sigs_preempt;

	if (_kio.ki_pid != getpid()) {
		errno = ENOTATTACHED;
		return(LAMERROR);
	}

	memcpy((char *) pkq->kq_fyi, (char *) _kio.ki_fyi,
			sizeof(_kio.ki_fyi));

	do {
		sigemptyset(&sigs_preempt);
		sigaddset(&sigs_preempt, LAM_SIGUSR);
		sigprocmask(SIG_BLOCK, &sigs_preempt, (sigset_t *) 0);

		if (_cio_kreq(pkq, pkr)) {
			sigprocmask(SIG_UNBLOCK, &sigs_preempt, 
					(sigset_t *) 0);
			return(LAMERROR);
		}

		sigprocmask(SIG_UNBLOCK, &sigs_preempt, (sigset_t *) 0);
/*
 * Do we need to follow a signal?
 */
		if (pkr->kr_signal) {
			_kio.ki_signal |= pkr->kr_signal;
			_ksig_follow();
		}
	} while (pkr->kr_reply == EINTR);

	return(0);
}

/*
 *	_cipc_cleanup
 *
 *	Function:	- frees up client resources
 */
void
_cipc_cleanup()

{
	_cio_cleanup();
}

/*
 *	cipc_preempt
 *
 *	Function:	- preempts myself
 *			- signal handler for LAM_SIGUSR
 */
static void
cipc_preempt()

{
	struct kreq	req;			/* kernel request */
	struct kreply	reply;			/* kernel reply */

	if (_kio.ki_pid != getpid()) return;

	req.kq_req = KQSURRENDER;
	req.kq_index = _kio.ki_index;

	if (_cio_kreq(&req, &reply)) return;

	if (reply.kr_signal) {
		_kio.ki_signal |= reply.kr_signal;
		_ksig_follow();
	}
/*
 * Vector LAM_SIGUSR to preemption routine.
 */
	if (_lam_signal(LAM_SIGUSR, cipc_preempt) == SIG_ERR) return;
}
