/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:protocol.c 12.0$ */
/* $ACIS:protocol.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ibm/rvd/server/RCS/protocol.c,v $ */

#ifndef lint
static char *rcsid = "$Header:protocol.c 12.0$";
#endif


#ifndef lint
static char rcsid_protocol_c[] = "$Header:protocol.c 12.0$";
#endif lint

/* Copyright 1984 by the Massachusetts Institute of Technology */
/* See permission and disclaimer notice in the file "notice.h" */
#include "notice.h"

/* This file contains the routines to construct RVD protocol packets
 * and send them out on the net.
 */

#include	<sys/types.h>
#include	<sys/param.h>
#include	<stdio.h>
#include	<errno.h>
#include	<netinet/in.h>
#include	<netinet/in_systm.h>
#include	<netinet/ip.h>
#include	<netinet/rvd.h>

#include	"rvd_types.h"
#include	"rvdadd.h"
#include	"custom.h"
#include	"obj.h"
#include	"queue.h"
#include	"packet.h"
#include	"extern.h"


rvd_error(pkt, ecode)

/* Convert the specified packet into an error packet and send it.  The ecode
 * argument is the error code.  The packet is assumed to already contain the
 * correct foreign host, drive, nonce, and index fields.
 * On return, the packet has been deallocated.
 */

register struct	rvd_pkt	*pkt;		/* packet to send */
register u_char	ecode;			/* error code */
{
	register struct	rvde	*erp;	/* pointer to error packet part */

	erp = (struct rvde *)&pkt->rp_rvd;

	erp->rvd_type = RVDERROR;	/* error packet type */
	erp->etype = ecode;		/* error code */
	erp->version = RVDVERSION;	/* always */

	pkt->rp_len = sizeof(struct rvde); /* packet size to send */

	pkt_complete(pkt);		/* fill in rest of fields */
	net_send(pkt);			/* send it and deallocate it */
}


spinack(pkt, nblocks, index, uid, ecode)

/* Convert the specified packet into a spinup ack and send it.  There's
 * some ugliness here in that, if ecode is nonzero, the packet is to
 * be sent with type RVDERROR instead of RVDSPACK.  This is a bug in the
 * protocol design.
 * On return, the packet has been deallocated.
 */

register struct	rvd_pkt	*pkt;		/* packet to send */
u_long	nblocks;			/* size of spunup drive in blocks */
u_long	index;				/* this connection's index */
register u_char	ecode;			/* error code, or RVDENOER if none */
{
	register struct	rvdsa	*sack;	/* pointer to spinack packet part */

	sack = (struct rvdsa *)&pkt->rp_rvd;

	if (ecode != RVDENOER) {	/* kludge way to return "identical
					 * pack already spun up" indication
					 */
		sack->rvd_type = RVDERROR;
		((struct rvde *)sack)->etype = ecode;
	} else
		sack->rvd_type = RVDSPACK;

	sack->version = RVDVERSION;
	sack->index = index;
	sack->vd_uid = uid;
	sack->nblocks = nblocks;
	sack->burst = BURST;		/* I love magic numbers... */
	sack->qlen = QLEN;
	sack->wb_factor = BLOCKFACTOR;	/* allowable blocks per packet */

	pkt->rp_len = sizeof(struct rvdsa);

	pkt_complete(pkt);
	net_send(pkt);
}


sdack(pkt, mode)

/* Convert the specified packet to a spindown acknowledgement and send it.
 * On return, the packet has been deallocated.
 */

register struct	rvd_pkt	*pkt;		/* packet to send */
u_char	mode;				/* mode disk was spun up in */
{
	register struct	rvdack	*sda;	/* spindown ack */

	sda = (struct rvdack *)&pkt->rp_rvd;

	sda->rvd_type = RVDACK;
	sda->mode = mode;
	sda->version = RVDVERSION;

	pkt->rp_len = sizeof(struct rvdack);

	pkt_complete(pkt);
	net_send(pkt);
}


rvd_wack(pkt, status, bcount)

/* Convert the specified packet to a write acknowledgement and send it.
 * On return, the packet has been deallocated.
 */

register struct	rvd_pkt	*pkt;		/* packet to send */
u_long	status;				/* write request status */
u_long	bcount;				/* number of blocks being acked */
{
	register struct	rvdwa	*wack;	/* write ack */

	wack = (struct rvdwa *)&pkt->rp_rvd;

	wack->rvd_type = RVDWACK;
	wack->version = RVDVERSION;
	wack->status = status;
	wack->bcount = bcount;

	pkt->rp_len = sizeof(struct rvdwa);

	pkt_complete(pkt);
	net_send(pkt);
}


block_complete(new, old, blockn, datalen)

/* Fill in the appropriate fields of the newly-allocated block packet in new
 * from those of the read request packet in old.  Blockn is the block number
 * of this block relative to the request.  Datalen is the size of the data
 * part of the block packet in bytes.
 * This routine depends on the important fields of the read request and
 * block packets being in the same place.
 */

register struct	rvd_pkt	*new;		/* new block packet to fill in */
register struct	rvd_pkt	*old;		/* old request packet */
u_long	blockn;				/* request-relative block number */
int	datalen;			/* length of data in bytes */
{
	register struct	rvdr	*newr;	/* temp pointer for copy */
	register struct rvdr	*oldr;	/* 	" 		 */

	newr = (struct rvdr *)&new->rp_rvd;
	oldr = (struct rvdr *)&old->rp_rvd;

	*newr = *oldr;			/* do the structure copy */
	newr->rvd_type = RVDBLOCK;	/* show it's a block packet */
	newr->blockn += blockn;		/* add in block number offset */

	new->rp_len = sizeof(struct rvdb) + datalen; /* set up length field */
	new->rp_fhost.s_addr = old->rp_fhost.s_addr; /* and foreign host */
}


rvd_block(pkt, status)

/* Finish filling in the specified block packet and then send it.
 * Most of the fields should have been filled in by block_complete.
 * On return, the packet has been deallocated.
 */

register struct	rvd_pkt	*pkt;		/* packet to send */
u_long	status;				/* write request status */
{
	register struct	rvdb	*block;	/* block part of packet */

	block = (struct rvdb *)&pkt->rp_rvd;

	block->rvd_type = RVDBLOCK;
	block->version = RVDVERSION;
	block->status = status;

	pkt_complete(pkt);
	net_send(pkt);
}
