/** PKTBACKET.H */

/* CONVENTIONS
 *
 * The originator of a packet is called Machine A
 * while the "destination" is called machine B
 *
 * This is obvious if machine A sends out a user packet request to 'B' followed
 * by some larger block of memory (ie A sends a "packet" to B)
 *
 * There is rise for some confusion however, since machine B may reply by sending back 
 * (DMA'ing) packets consisting of return memory (which may be any length) and a result
 * So, machine 'A', which then makes 'B' the 'sender' and 'A' the 'destination'
 *
 * with the above in mind the following is adopted
 *
 * No matter who originated the packet, the "sender" machine for a particular packet is
 * always the machine who is actually sending that packet (in the above context, both
 * 'A' and 'B' may be 'senders')
 *
 * Likewise for the 'destination' machine (which "recv's" packets)
 *
 * The original packet sender (the machine which sent a user packet request) is always
 * called the "originator" or the "origin" machine
 *
 * The machine for which the original packet is destined for is always called the
 * "doofus" machine.
 *
 * If an "emem" is to be unused then the "len" field should be set to NULL
 *
 *
 * No send buffer is *ever* trashed when being sent out (including the stdreq
 * type) however the "internal" pkthead structure should not be accessed at all
 *
 */


/** THE STANDARD MESSAGE WHICH IS PASSED AROUND */
struct StdMsg {
	struct	Message msg;
	long	type;					/* what this message represents */
	union {
		struct	Remember *mem;		/* have to know what you are doing! */
		long	alloclen;
	};
	long	result;					/* the result of this message */
};

/** StdMsg->type DEFINES FOR THIS PROGRAM */

#define TYPE_INTERNAL_SEND				-2
#define TYPE_INTERNAL_DOOFUSRECV		-1

#define TYPE_RECEIVED		0
#define TYPE_RETURN			1
#define TYPE_SENDDMA		2
#define TYPE_ORIGINALPKT	3
#define TYPE_REMOVE			4
#define TYPE_QUIT			69


/** return code from all of the asm routines */
#define PAR_OK 0

/** return codes from the "setup" asm routines */
#define PAR_ERROR_TIMEOUT 1
#define PAR_ERROR_BUSY 2

/** extra return codes from the C-code packet handlers */
#define PAR_ERROR_CHKSUM	3
#define PAR_ERROR_REJECT	4
#define PAR_ERROR_NOMEM		5

#define PAR_ERROR_QUIT		6

#define PAR_ERROR_CALLER	7	/* original_pkt not constructed properly */

/** PACKET HEADER SENT OUT - INTERNAL USE */
struct PktHead
{
	short	phchk;
	short	bodychk;
	union {
		long	majic_code;		/* this is the majic_code if it is a DMA pkt */
		long	totallength;	/* OR this if a request (userreq + sendbody) */
	};
	short	pktlen;				/* length of this packet if this is not the same as
								 * "length" then this read packet */

	short	server;				/* which server this is destined for, or if its a
								   DMA (or DMAreturn) */
	short	sender;				/* currently only the lower byte is used */
	short	pad;
};


/**
 **
 ** LONG's SENT BACK IMMEDIATELY - INTERNAL USE *ONLY*
 **
 **/

/** MAJIC ACCEPT AND ERROR CODES */
#define MAJIC_ACCEPT 0x5350614B
#define MAJIC_REJECT 0x68456144
#define MAJIC_NOMEM  0x7E4D656D
#define MAJIC_NOSERVER 0x4E734572

/** ACTUALLY ANYTHING ELSE CAN BE CONSIDERED A CHKSUM ERROR */
#define MAJIC_CHKSUMERR 0x64414D6E


/** MEMORY NODE TYPE CONCEPT */
struct EMem {
	void	*mem;
	long	len;
	void	(*routine)(void *);
};

/** STANDARD defines for the "routine" all others will be "called" */
#define EMEM_NOROUTINE ((void (*)(void *))NULL)
#define EMEM_RETURNMSG ((void (*)(void *))-1L)


/**
 **
 ** YOU *MUST* HAVE THIS StdRequest STRUCTURE AT THE START OF ALL REQUEST
 ** PACKETS, OTHERWISE THE SENDER WILL STUFF UP!
 ** HOWEVER THERE IS NO NEED TO FILL ANY OF IT IN (IT IS AUTOMATICALLY FILLED)
 **
 **/

struct StdReq
{
	long	majic_code;			/* source machine keeps track of actual addresses */
	long	result_len;			/* things to be DMA'd back */
	long	return_len;			/* more stuff to be DMA'd back */
};


/**
 **
 ** PACKET-MESSAGE
 **
 **/

struct PktMsg {
	struct	StdMsg		smsg;			/* fill this in! */

/** INTERNAL STUFF UNLESS OTHERWISE STATED */
	struct	PktMsg		*next, **stuff;	/* list of packets requiring DMAwriting */

	struct	EMem		send;			/* Fill in mem & len */
	struct	EMem		recv[3];		/* Fill in mem & len of 1 & 2 */

	long	remote_majic;				/* majic code of the remote machine */

	struct	PktHead		pkthead;		/* actual head sent off */
	short	retriesleft;

	short	dest;						/* originator FILL this in! */

	void	*send_body;					/* originator FILL this in */
	long	send_len;					/* originator FILL this in */

	/** LONG WORD ALIGNED */
};


struct PktMsgRecv {
	struct	PktMsg pm;				/* entire packet message you can use for sending */

	struct StdReq	*req_body;		/* pointer to the std-request followed by the */
	long	req_len;				/* user part (req_len is COMPLETE length) */

	struct	MsgPort	*dmaport;		/* put msg's here to DMA stuff back */

	union {
		struct {
			void	(*user1)(struct PktMsgRecv *);	/* usually a routine! */
			long	user2;				/* anything you want */
		} user;					/* once the user gets this packetmessage */
		struct {
			long	ourmajic;
			struct MsgPort *servport;	/* port of the dest service */
		} internal;				/* before we send the message off */
	};
};




/**
 **
 ** PACKET TYPES (PktHead.server)
 ** SERVER TYPES < 0 ARE FOR INTERNAL USE
 ** CURRENTLY ONLY THE FIRST 64 ARE SUPPORTED (JUST RECOMPILE WITH A BIGGER TABLE
 ** IF YOU REQUIRE MORE)
 **
 **/

#define SERV_MAJIC	-1				/* write doofus's majic to originator */
#define SERV_SEND	-1				/* originator writes send_body to doofus */
#define SERV_RESULT	-2				/* doofus DMA's into originator */
#define SERV_RETURN -3				/* doofus DMA's into originator & originator
									 * is expected to return (& doofus to free pkt) */

#define SERV2INDEX(a) (-(a)-1)		/* convert above servers to an index */

/** CURRENT PACKET SERVERS IN EXISTANCE */
#define SERV_GFX	  15			/* network gfx server */

#define SERV_FILE	  20			/* file system server */
#define SERV_DEVICE   21			/* device mirror */
#define SERV_AREXX    22			/* arexx mirror */
#define SERV_TEST	  23			/* message test service */

#define SERV_TRACKER  30			/* sample tracking service */



/**
 **
 ** WHEN YOUR PROGRAMS NEED TO REGISTER WITH US...
 **
 **/

struct RegisterMsg {
	struct	StdMsg smsg;					/* look at smsg.return for the return value */
	long	command;						/* command to perform, see below */
	long	arg1;							/* depending on the command */
	long	arg2;
	long	arg3;
	void	*stuff;							/* usually a pointer to extra stuff (if needed) */
};



/** registermsg->command types */
#define RM_ADD_SERVER	1			/* add "arg1" as the port serviced by message port "arg2", "arg1" returned with the address of the "comport" or NULL if error */
#define RM_REMOVE_SERVER 2			/* "arg1" is the port server to remove (return is what it used to be) */

/******************************************************************************/


/**
 **
 ** MACROS TO INIT THE ABOVE STRUCTURES
 **
 ** Use the "PREPs" if the structure has been "INIT'd" at least once
 ** otherwise, when building a structure from scratch use the "INIT"s to write all
 ** the needed values in
 **
 ** Note that the "mem" field in StdMsg is left to your own manipulation (it is not
 ** touched)
 **
 **/

/** free a PktMsgRecv sent to a service (the service must use this) */
#define FREE_PKTMSGRECV(pmr) \
	\
	FreeMem(pmr, (pmr)->pm.smsg.alloclen)


/** WRITE DEF VALUES INTO A MESSAGE STRUCTURE */
#define INIT_MSG(a,b,c) \
	\
	((a)->mn_Node.ln_Type = NT_MESSAGE, \
	 (a)->mn_Node.ln_Name = NULL, \
	 (a)->mn_ReplyPort = b, \
	 (a)->mn_Length = c)

#define INIT_REGISTERMSG(rm_o, replyport_o, com_o, a1_o, a2_o, a3_o) \
	\
	(INIT_MSG(&((rm_o)->smsg.msg), replyport_o, sizeof(struct RegisterMsg)), \
	(rm_o)->arg1 = (long)a1_o, \
	(rm_o)->arg2 = (long)a2_o, \
	(rm_o)->arg3 = (long)a3_o, \ 
	(rm_o)->command = com_o )


/**
 ** THE FOLLOWING MACROS USED INTERNALLY AND SHOULD NOT BE NEEDED FOR SERVICES
 **/

#define START_TIMER(a,b,c) \
	\
	((a)->tr_node.io_Command = TR_ADDREQUEST,\
	(a)->tr_time.tv_secs = b,\
	(a)->tr_time.tv_micro = c,\
	SendIO(a))

#define INIT_EMEM(emem_o, mem_o, len_o, code_o) \
	\
	((emem_o)->mem = mem_o,\
	(emem_o)->len = len_o,\
	(emem_o)->routine = code_o)

/**
 ** NOTE: if you are reusing a pktmsg then be sure to use these macros each time before
 ** sending since many of the values are trashed once the pkt has been sent
 **
 **/

#define INTERNAL_PREP_PKTMSG_SEND(pm, routine_o, \
							mem_o, length_o, lengthormajic_o) \
	\
	(INIT_EMEM(&(pm)->send, mem_o, length_o, routine_o),\
	(pm)->pkthead.majic_code = lengthormajic_o)

/*
 * actually your "service" task can use this too
 * note that retries left set to -1 means to use the default amount - you can
 * change that to a positive number instead, but if your "send" needs to be
 * split up, subsequent packets will contain the old retry count if the amount
 * of "retriesleft" falls below the default amount - effectively giving you the
 * opportunity to have a very large number of retries
 *
 * The idea is that you call this on the PktMsgRecv structure a server sends to
 * your service
 *
 */

#define INTERNAL_PREP_PKTMSG_DMA(pm, routine_o, destservice_o, mem_o, length_o) \
	\
	((pm)->pkthead.server = destservice_o,\
	(pm)->retriesleft = -1,\
	INTERNAL_PREP_PKTMSG_SEND(pm, routine_o, mem_o, \
								length_o, (pm)->remote_majic))


/** this is internal *ONLY* */
#define INTERNAL_PREP_PKTHEAD(ph, body_o, bodylen_o) \
	\
	((ph)->phchk = 0, \
	(ph)->pktlen = bodylen_o,\
	(ph)->sender = cir.machine,\
	(ph)->bodychk = ChkSum(body_o, bodylen_o), \
	(ph)->phchk = ChkSum(ph, sizeof(struct PktHead)))



/*
 * use this to initialize a packet that you wish to send to a service on another
 * machine
 */

#define PREP_PKTMSG_ORIGINAL(pm, destmachine_o, service_o, \
		req_o, reqlen_o, sendmem_o, sendlen_o, recv_o, recvlen_o, res_o, reslen_o) \
	\
	((pm)->smsg.type = TYPE_ORIGINALPKT,\
	(pm)->send.mem = req_o,\
	(pm)->send.len = reqlen_o,\
	(pm)->recv[1].mem = recv_o,\
	(pm)->recv[1].len = recvlen_o,\
	(pm)->recv[2].mem = res_o,\
	(pm)->recv[2].len = reslen_o,\
	(pm)->pkthead.server = service_o,\
	(pm)->dest = destmachine_o,\
	(pm)->send_body = sendmem_o,\
	(pm)->send_len = sendlen_o)


#define INIT_PKTMSG_ORIGINAL(pm, replyport_o, destmachine_o, service_o, \
		req_o, reqlen_o, sendmem_o, sendlen_o, recv_o, recvlen_o, res_o, reslen_o) \
	\
	(INIT_MSG(&(pm)->smsg.msg, replyport_o, sizeof(struct PktMsg)),\
	PREP_PKTMSG_ORIGINAL(pm, destmachine_o, service_o, \
		req_o, reqlen_o, sendmem_o, sendlen_o, recv_o, recvlen_o, res_o, reslen_o))
	

/** FOR THE SERVICE TO SEND BACK SOME MEMORY */
#define PREP_PKTMSG_SENDDMA(pm, servtype_o, buffer_o, len_o) \
	\ 
	((pm)->smsg.type = TYPE_SENDDMA,\
	(pm)->send.mem = buffer_o,\
	(pm)->send.len = len_o,\
	(pm)->pkthead.server = servtype_o)


#define PUT_STDMSG(smsg_o, port_o, type_o) \
	\
	((smsg_o)->type = type_o,\
	PutMsg(port_o, &(smsg_o)->msg))


#define REPLY_STDMSG(sm) \
	\
	((sm)->type = TYPE_RETURN,\
	ReplyMsg(&(sm)->msg))

