/* DPSUP.H - Device sub-Process Support definitions (OSD) for KLH10
*/
/* $Id: dpsup.h,v 2.4 2002/04/24 07:46:15 klh Exp $
*/
/*  Copyright © 1994, 2001 Kenneth L. Harrenstien
**  All Rights Reserved
**
**  This file is part of the KLH10 Distribution.  Use, modification, and
**  re-distribution is permitted subject to the terms in the file
**  named "LICENSE", which contains the full text of the legal notices
**  and should always accompany this Distribution.
**
**  This software is provided "AS IS" with NO WARRANTY OF ANY KIND.
**
**  This notice (including the copyright and warranty disclaimer)
**  must be included in all copies or derivations of this software.
*/
/*
 * $Log: dpsup.h,v $
 * Revision 2.4  2002/04/24 07:46:15  klh
 * Change dpx_cnt from int to size_t, modify prototypes to match
 *
 * Revision 2.3  2001/11/10 21:28:59  klh
 * Final 2.0 distribution checkin
 *
 */

/*
	This file defines the IPC mechanism used to communicate between
virtual device code in the KLH10 and the various sub-processes that
may be needed to carry out their operations.  For our purposes, "input"
and "output" are defined relative to the virtual PDP10; input is data
coming from a device to the 10, and output is generated by the 10 for
transfer to a device.

	Note that the sub-processes may, in various configurations, be
any of:

(1) Full subprocess (forked child)
	Possible on most process-oriented platforms (Unix).
	One process for output; another process for input if
		asynch input is possible for device.
	Signalling DP done with either user-defined OS signal
		or OS semaphore.
	Signalling 10 done with user-defined OS signal (10 cannot
		waste time polling semaphores).
	DP reset done with OS signal and/or process kill.

(2) Threaded process within KLH10
	Ideal but only possible if platform has reliable threads support.
	One thread for output; another for input if asynch input is
		possible for device.
	Signalling DP done by condition vars (fast semaphores).
		OS semaphores also possible though slower.
	Signalling 10 done with direct lock & intf setting.
		OS signals also possible but slower.
	DP reset done with condvar setting and/or thread kill.

(3) Asynchronous (non-blocking interrupt-driven) within KLH10
	Not always possible, even on Unix (disk in particular).
	No distinct DP process/thread, so no DP/10 comm needed.
	I/O never blocks; OS signals given when input avail or
		output ready.  Signal handler can either perform I/O
		or set flag for main loop.
	DP reset done directly.

(4) Synchronous within KLH10
	Maximally portable, but slowest mechanism.
	No distinct DP process/thread, so no DP/10 comm needed.
	Output always blocks; input is polled.
	DP reset done directly.

Only one mechanism (DP_XT_MSIG) is actually implemented at present.

*/

/*
	The DP IPC mechanism implemented here has some remote
similarities to the 10-11 communication protocol.  Two identical
comm/transfer regions are used, one for each direction to and from the
subprocess.  Messages consist of simple commands that may or may not
point to large quantities of information elsewhere.

To send a message:
	Sender waits until Ready flag is clear.
	Sender sets up all necessary data for the message, then sets
		Ready flag to -1 and signals receiver.
	If result is important, can wait until Ready is clear, then
		examine Result value.

To receive a message:
	Receiver waits until signaled and Ready flag is set.
	Receiver carries out operation specified by data, then sets
		Result value and clears Ready.
	Receiver signals sender.


Should regions be arranged so each is R/W for sender, and RO for receiver?
Can distribute variables appropriately.  Prevents wild subproc from
messing up... a little.

For now, keep everything in one mutually R/W region for simplicity.

*/

#ifndef DPSUP_INCLUDED
#define DPSUP_INCLUDED 1

#ifdef RCSID
 RCSID(dpsup_h,"$Id: dpsup.h,v 2.4 2002/04/24 07:46:15 klh Exp $")
#endif

#ifndef OSDSUP_INCLUDED
# include "osdsup.h"	/* For osintf_t etc */
#endif

#define DP_XT_MSIG 1	/* Shared mem, use signal for doorbell */
#define DP_XT_MSEM 2	/* Shared mem, use semaphore for doorbell */
#define DP_XT_THCV 3	/* Same mem, use thread condition var */

#if 0
union dpcxmech {
    struct dpc_xt_msig {
	ospid_t dpxt_pid;	/* S: Owner/Sender */
	osshm_t dpxt_mem;	/* S: Shared mem identifier */
	oscad_t dpxt_sadr;	/* S: Loc in sender address space */
	ossig_t dpxt_sig;	/* Signal # to use for doorbell */
    } msig;
};
#endif

/* DP one-way transfer region
**	Ready flag is set to -1 when sender has deposited a message for
**		the reader.  It is cleared to 0 when receiver has
**		processed the message.
**
*/
				/* C=Creator, S=Sender, R=Receiver */
struct dpx_s {
    int dpx_type;		/* C: Type of comm mechanism */
#if 0
    union dpx_osd;		/* S/R: OSD stuff for comm mech */
#else
    int dpx_waktyp;		/* R: How to wake up rcpt */
    int dpx_wakflg;		/* R/S: R sets 1 when trying to do wake */
    int dpx_waksig;		/* C: Signal # to use */
    sigset_t dpx_wakmsk;	/* C: Mask for signal # */
    int dpx_wakpid;
    unsigned char *dpx_rbuf;	/* R: R's ptr into buffer */

    int dpx_dontyp;		/* S: How to Ack sender */
    int dpx_donflg;		/* S/R: S sets 1 when trying to do ack */
    int dpx_donsig;		/* C: Signal # to use */
    sigset_t dpx_donmsk;	/* C: Mask for signal # */
    int dpx_donpid;
    unsigned char *dpx_sbuf;	/* S: S's ptr into same buffer */
#endif

    size_t dpx_len;		/* C: Buffer length */
    size_t dpx_off;		/* C: Buffer offset from beg of segment */
    volatile
	osintf_t dpx_rdyf;	/* S/R: Ready flag */
    int dpx_res;		/* R: Result value */
    int dpx_cmd;		/* S: Command */
    size_t dpx_cnt;		/* S: Count of data bytes */

    /* Args of various types -- later could be union */
    int dpx_int;
    long dpx_long;
    unsigned char *dpx_ucp;
#if 0
    paddr_t dpx_pa;
    w10_t dpx_w;
    dw10_t dpx_dw;
#endif
};
typedef struct dpx_s dpx_t;

/* DP common area, shared between Main (superior) and DP (subproc)
**	
*/
struct dpc_s {
    char dpc_magic[4];		/* M: Magic chars saying this is DPC mem seg */
    int dpc_fmtver;		/* M: Format version  */
    char dpc_id[8];		/* M: App identifier chars */
    int dpc_flags;		/* M/DP: Flags */
    int dpc_debug;		/* M: Debug flag (separate for efficiency) */
    struct dpx_s dpc_todp;	/* M:  To subproc from KLH10 */
    struct dpx_s dpc_frdp;	/* DP: From subproc to KLH10 */

    /* Various other DPC info */

    /* Device-dependent stuff, extensible to arbitrary size */
};

/* Defs for DPC contents */

#define DPC_MAGIC "DPM"

#define DPC_VERSION(maj,min,pat) (((maj)<<10) | ((min)<<5) | (pat))
#define DPC_GV_MAJ(a) (((a)>>10)&037)
#define DPC_GV_MIN(a) (((a)>>5)&037)
#define DPC_GV_PAT(a) (((a)>>0)&037)

#define DPSUP_VERSION DPC_VERSION(1,2,0)	/* This version of DPSUP */

#define DPCF_MEMLOCK	0x1	/* M wants DP to lock its mem if possible */


/* DP handle - private memory */
struct dp_s {
    int dp_type;
    long dp_shmid;		/* Change to osmid_t later */
    struct dpc_s *dp_adr;
    int dp_chpid;		/* Change to ospid_t */
};
typedef struct dp_s dp_t;

/* General Facilities */

/* Called from superior (KLH10) */

int dp_init (dp_t *dp, size_t, int, int, size_t in,
				int, int, size_t out);
int dp_start(dp_t *dp, char *pgm);
int dp_stop (dp_t *dp, int timeout);
int dp_reset(dp_t *dp);		/* What would this do? */
int dp_term (dp_t *dp, int timeout);
void dp_exit(dp_t *dp, int res);


/* Called from subprocess (dp) */

int dp_main (dp_t *dp, int argc, char **argv);


/* Called from both for communications */

/* struct dpx_s *dp_dpxto(dp_t *); */	/* "To DP" direction */
/* struct dpx_s *dp_dpxfr(dp_t *); */	/* "From DP" direction */
#define dp_dpxto(dp) (&((dp)->dp_adr->dpc_todp))
#define dp_dpxfr(dp) (&((dp)->dp_adr->dpc_frdp))

int  dp_xstest (dpx_t *);		/* TRUE if can send */
void dp_xsblock(dpx_t *);		/* Block for later testing */
int  dp_xswait (dpx_t *);		/* Wait until can send */
unsigned char *
    dp_xsbuff(dpx_t *, size_t *);	/* Get buffer for send data */
void dp_xswake(dpx_t *);		/* Send; say message ready */
void dp_xsend(dpx_t *, int, size_t);	/* Send cmd & data */

int  dp_xrtest (dpx_t *);		/* TRUE if can receive (have input) */
void dp_xrblock(dpx_t *);		/* Block for later testing */
int  dp_xrwait (dpx_t *);		/* Wait until can rcv (have input) */
unsigned char *
     dp_xrbuff(dpx_t *, size_t *);	/* Get pointer to data */
void dp_xrdone(dpx_t *);		/* Receive; say message done */
void dp_xrdoack(dpx_t *, int);		/* Receive; say done, w/res */
int  dp_xrcmd(dpx_t *);			/* Get command for msg */
size_t dp_xrcnt(dpx_t *);		/* Get cnt for msg */

/* Misc internals */
void dp_sigwait(void);			/* Wait until any sig happens */
void dp_sleep(int);			/* Sleep for N seconds */

/* Facilities for DPCXT_MSIG */

#define dp_xtmsig_stest(dpx) ((dpx)->dpx_rdyf == 0)	/* TRUE if can send */
#define dp_xtmsig_swake(dpx) (((dpx)->dpx_rdyf = 1),	\
			((dpx)->dpx_wakflg = 1),	\
			kill((dpx)->dpx_wakpid, (dpx)->dpx_waksig))
#define dp_xtmsig_sblock(dpx) dp_sigwait()
#define dp_xtmsig_swait(dpx) \
		while (!dp_xtmsig_stest(dpx)) dp_xtmsig_sblock(dpx)
#define dp_xtmsig_sbuff(dpx, asiz) \
		((asiz ? (*(asiz) = (dpx)->dpx_len) : 0), (dpx)->dpx_sbuf)
#define dp_xtmsig_send(dpx, cmd, cnt) \
	((dpx)->dpx_cmd = (cmd), (dpx)->dpx_cnt = (cnt), dp_xtmsig_swake(dpx))

#define dp_xtmsig_rtest(dpx) ((dpx)->dpx_rdyf != 0)	/* TRUE if can recv */
#define dp_xtmsig_rdone(dpx) (((dpx)->dpx_rdyf = 0),	\
			((dpx)->dpx_donflg = 1),	\
			kill((dpx)->dpx_donpid, (dpx)->dpx_donsig))
#define dp_xtmsig_rdoack(dpx, res) ((dpx)->dpx_res = (res), \
			dp_xtmsig_rdone(dpx))
#define dp_xtmsig_rblock(dpx) dp_sigwait()
#define dp_xtmsig_rwait(dpx) \
		while (!dp_xtmsig_rtest(dpx)) dp_xtmsig_rblock(dpx)
#define dp_xtmsig_rbuff(dpx, asiz) \
		((asiz ? (*(asiz) = (dpx)->dpx_len) : 0), (dpx)->dpx_rbuf)
#define dp_xtmsig_rcmd(dpx) ((dpx)->dpx_cmd)
#define dp_xtmsig_rcnt(dpx) ((dpx)->dpx_cnt)

#if 0
/* For device to register its dp with 10 via device vector */
int dpcxt_msig_register(struct dpc_s *dpc, struct device *d);
#endif

/* Misc auxiliary unrelated to DP support but one that exists and
   which all DP procs need anyway (to avoid needing full OSDSUP module)
*/
extern char *dp_strerror(int);

#endif /* ifndef DPSUP_INCLUDED */
