/* lun.c - handle LUN allocation		*/
/* DECUS C , RSX-11M+ flavour			*/

/*)LIBRARY
*/

#include <algol68.h>
#include <cx.h>
#include <qiofun.h>

#define	IQ_X (1)	/* doesn't ANYBODY define this? somewhere? */

#define local

#ifdef	DOCUMENTATION
title	LUN	Logical Unit Number control
index	LUN
index	Lunattach()
index	Lundetach()
index	Lunreserve()
index	Getbit()
index	Putbit()
index	Logical Unit Number
index	Attatchment of LUN
index	Detachment of LUN
usage
	 
	 int	lun;	/* -ve IE_??? error code or +ve LUN */
	 char *	devstr;	/* typically "DDnnn" */
	 int	result;	/* -ve IE_??? error, +ve success */
	 
	 lun = lunattach(devstr);
	 {compute...}
	 result = lundetach(lun);
	 lunreserve(42);	/* Lun 42 marked in use */
	 	 
description
	This (kludge) module exists to solve a vexing problem.
	When we use various software libraries in a single task,
	each library must be told what LUNS it may use. Otherwise
	different parts of a task "hijack" LUNS from other parts
	with disasterous consequences. These functions try to
	keep a "scoreboard" of what LUNS are free and what are in
	use.

	RSX seems to have no easy way for a task to know what LUNS
	are "in use". I would accept "IO.ATTached" as a definition of
	"in use". If I have missed a trick, PLEASE re-submit this
	module with your idea of how to find the highest un-attached
	LUN from a non-priv task.

	This module clobbers event flag 2.
	It uses Getbit() and Putbit().

	Lunattach() will find an allegedly free LUN, assign it to
	a given device name, and attach it (with error rather than
	waiting for it to be free).

	Lundetach() will IO.DETach a LUN, then mark it on the
	scoreboard as available for use by a future Lunattach().

	If you don't want IO.ATTatchment with Lunattach() then
	please IO.DET the device yourself.

	LUNATTACH()

	Assign,
	then attach, a LUN (previously not in use by your task) to the
	device named in devstr. Devstr does not require ':'. Beware
	that a device name with node name will be misinterpreted viz.
	"MYNODE::LB42:MUMBLE.UPU;6" will be interpreted as device MY0:
	because the rules are: first two characters are device name
	and any characters starting at third character that can be
	interpreted as an octal number form the device number.

	Return the LUN number (positive) if we suceeded.
	This means that we could assign the LUN to the device and
	also attach it.

	Return an RSX error code if we failed.
	Return IE.ULN (Unassigned LUN) if we had no available LUNs
	for this task, right now.

	LUNDETACH()

	IO.DETatch a LUN, returning the error code from DSW or
	I/O status block [0]. A positive return means OK.

	LUNRESERVE()

	Mark a Lun as "in use" in the scoreboard. Do NOTHING else.
	Useful for people who have IO.ATTed in some other way or who
	just don't want us to mess with that LUN. If this is called first,
	then it does the right thing by setting up the scoreboard correctly,
	then marking a Lun as "in use".

	LUNFIX()

	Lunfix() is an internal routine that knocks all the "lun in
	use" bits down in the scoreboard. It is called automagically
	by the other routines when needed. It only works once
	then fakes out if it is called again.
internal
	For Lunattach(), distinguish two ideas:
	(1) how we determine a LUN is eligible
	for assignment; (2) which order we try to assign LUNs.

	A LUN is available if the bloody scoreboard says it is.

	The first time Lunattach() is called we set up the scoreboard
	by setting up some "used" bits. We can't
	create this scoreboard at compile time, because until
	run-time we don't know how many LUNs are available to a task.
	We also set up the scoreboard if the user calls Lundetach()
	first, because some fool might one day reasonably want to do this.

	We find a free LUN by trying every LUN (highest to lowest)
	until we find one that the scoreboard says is OK to use.
	This is crude but works.

	Lunisb[] holds the I/O status block from the last IO.DET or
	IO.ATT to assist error recovery for particularly curious
	callers.
bugs
	This is for RSX only. May later evolve for
	VMS, RSTS, RT, XXDP, UNIX, DOS etc.

	On any NORMAL device, once you have attached it, then
	you can't attach it again: you get IO.DAA "device already attached"
	errors! So programs can't blindly attache anything to any Lun.

	Lets make it understand node names one day, and skip past
	them to the device name, or even get the right device at the
	right node assigned to the lun.
#endif

extern int _nluns;	/* RSX11M taskbuilder: LUNs are [1:(.nluns)]	*/

#define	EFN	(2)	/* event flag to clobber			*/

local	int	lunuse[32]	/* 256 bits. 1 = in-use. 0 = available	*/
	= {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
		-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
local	BOOL	lungo = FALSE;	/* TRUE when lunuse[] is valid		*/
				/* also a dummy parameter list to QIO	*/

	int	lunisb[2];	/* I/O status block for ATT,DET		*/

local	lunfix()	/* internal use: set up lunuse[] if needed	*/
BEGIN
register	int	i;

	IF	(!lungo)
	THEN	lungo = TRUE;
		FOR	(i=_nluns; i>3; i--)	/* LUNS 1:3 are for C	*/
		DO	putbit(lunuse,i,0);	/* mark as available	*/
		OD
		/* here with LUN 0 dedicated, but some LUNs free	*/
	FI
END

	lunreserve(Lun)
int	Lun;
BEGIN
	Luncheck(Lun);
	Lunfix();
	putbit(lunuse,Lun,1);
END

	luncheck(Lun)
int	Lun;
BEGIN
	IF	(Lun&0xFF00)
	THEN	abort();		/* ridiculous LUN		*/
	FI
END

int	lunattach	(devstr)
char *	devstr;		/* string defining device			*/
			/* for RSX: we expect exactly 2 letters, then	*/
			/* an OCTAL unit number.			*/
BEGIN
/*
 *	We expect to use RSX error codes if the device string is ill
 *	conditioned. So we assume it is OK and RSX detects all the
 *	bad news.
 */
int	devnam;
int	devunt;
int	lun;
char	result;	/* char catches -ve BYTE error codes */

	lunfix();
	copy(&devnam,devstr,2);
	devunt = 0;			/* assume 0 if sscanf fails */
	sscanf(devstr+2,"%o",&devunt);
	FOR	(lun=_nluns;  lun;  lun--)
	DO	IF	(!getbit(lunuse,lun))
		THEN	putbit(lunuse,lun,1);	/* mark as used		*/
			IF	(((result=alun(lun,devnam,devunt))&0xFF)==IS_SUC)
			THEN	IF	(((result=qiow(IO_ATT|IQ_X,lun,EFN,lunisb,0,&lungo))&0xFF)==IS_SUC)
				THEN	result  = lunisb[0];
				FI
			FI
			break;
		FI
	OD
	IF	(result<0)
	THEN	lun = result;	/* sign extend to be really -ve */
	FI
	return(lun);
END

int	lundetach(lun)			/* detach and mark as free	*/
int	lun;				/* LUN				*/
BEGIN
char	result;				/* sign extends -ve byte errors	*/

	Luncheck(lun);
	lunfix();
	putbit(lunuse,lun,0);		/* 0 = available		*/
	IF	(((result=qiow(IO_DET,lun,EFN,lunisb,0,&lungo))&0xFF)==IS_SUC)
	THEN	result  = lunisb[0];
	FI
	return(result);			/* sign extend			*/
END

/* end: lun.c */
