head     56.4;
access   paws bayes jws quist dew jwh;
symbols  ;
locks    ; strict;
comment  @# @;


56.4
date     93.01.27.13.53.50;  author jwh;  state Exp;
branches ;
next     56.3;

56.3
date     93.01.27.12.25.56;  author jwh;  state Exp;
branches ;
next     56.2;

56.2
date     92.05.26.17.07.07;  author cfb;  state Exp;
branches ;
next     56.1;

56.1
date     91.11.05.10.01.31;  author jwh;  state Exp;
branches ;
next     55.1;

55.1
date     91.08.25.10.36.21;  author jwh;  state Exp;
branches ;
next     54.1;

54.1
date     91.03.18.15.37.01;  author jwh;  state Exp;
branches ;
next     53.1;

53.1
date     91.03.11.19.35.35;  author jwh;  state Exp;
branches ;
next     52.1;

52.1
date     91.02.19.09.21.44;  author jwh;  state Exp;
branches ;
next     51.1;

51.1
date     91.01.30.16.20.20;  author jwh;  state Exp;
branches ;
next     50.1;

50.1
date     90.10.29.16.34.12;  author jwh;  state Exp;
branches ;
next     49.1;

49.1
date     90.08.14.14.17.51;  author jwh;  state Exp;
branches ;
next     48.1;

48.1
date     90.07.26.11.24.26;  author jwh;  state Exp;
branches ;
next     47.1;

47.1
date     90.05.14.11.10.54;  author dew;  state Exp;
branches ;
next     46.1;

46.1
date     90.05.07.08.57.59;  author jwh;  state Exp;
branches ;
next     45.2;

45.2
date     90.04.25.17.25.20;  author dew;  state Exp;
branches ;
next     45.1;

45.1
date     90.04.19.16.05.50;  author jwh;  state Exp;
branches ;
next     44.1;

44.1
date     90.04.01.22.25.04;  author jwh;  state Exp;
branches ;
next     43.1;

43.1
date     90.03.20.14.15.46;  author jwh;  state Exp;
branches ;
next     42.5;

42.5
date     90.03.14.11.00.43;  author dew;  state Exp;
branches ;
next     42.4;

42.4
date     90.03.13.16.42.35;  author dew;  state Exp;
branches ;
next     42.3;

42.3
date     90.03.13.16.08.57;  author dew;  state Exp;
branches ;
next     42.2;

42.2
date     90.03.08.13.58.15;  author dew;  state Exp;
branches ;
next     42.1;

42.1
date     90.01.23.17.59.43;  author jwh;  state Exp;
branches ;
next     41.5;

41.5
date     90.01.18.23.01.36;  author dew;  state Exp;
branches ;
next     41.4;

41.4
date     90.01.12.17.58.58;  author dew;  state Exp;
branches ;
next     41.3;

41.3
date     90.01.10.14.56.31;  author dew;  state Exp;
branches ;
next     41.2;

41.2
date     90.01.05.10.17.12;  author dew;  state Exp;
branches ;
next     41.1;

41.1
date     89.12.22.11.41.33;  author jwh;  state Exp;
branches ;
next     40.7;

40.7
date     89.11.29.10.28.33;  author dew;  state Exp;
branches ;
next     40.6;

40.6
date     89.10.24.13.20.06;  author dew;  state Exp;
branches ;
next     40.5;

40.5
date     89.10.18.17.03.19;  author dew;  state Exp;
branches ;
next     40.4;

40.4
date     89.10.13.10.53.43;  author dew;  state Exp;
branches ;
next     40.3;

40.3
date     89.10.13.09.57.33;  author dew;  state Exp;
branches ;
next     40.2;

40.2
date     89.10.11.16.54.32;  author dew;  state Exp;
branches ;
next     40.1;

40.1
date     89.09.29.12.02.41;  author jwh;  state Exp;
branches ;
next     39.1;

39.1
date     89.09.26.16.47.32;  author dew;  state Exp;
branches ;
next     1.1;

1.1
date     89.09.14.10.22.14;  author dew;  state Exp;
branches ;
next     ;


desc
@MESSAGES is a SCSI Generic Driver Module.
@


56.4
log
@
pws2rcs automatic delta on Wed Jan 27 13:14:25 MST 1993
@
text
@{
	SCSI Messages Module.

	Contains the SCSI driver message level interface.
	This includes all actions as defined by the network level, that
	is to: 1) set up a path between the host and target,
	       2) maintain that path (handle comminication, transmission
		  errors, disconnect/reconnect)
	       3) terminate the path.

	The transport (command) level interacts with the network (message)
	level to set up paths, handle transmission errors, etc.

	The SCSI Driver is state machine driven.
	The State Machine actually makes all of the calls, and the state
	tables enforce the division of labor (commands/messages) as defined
	above.
}
module MESSAGES;

import SCSI_DEFS, SCSI_UTILS, HWI_UTILS, OSD_LL;

export
	Procedure MsgWaitTimeOrFree     (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgSelect             (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgIdentifyMsg        (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgInitSynchParms     (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgTargetSynchParms   (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgGetMsg             (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgRejectMsg          (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgSetXfer            (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgDoXfer             (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgSawErrMsg          (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgPrepareDisc        (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgSetDisc            (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgPrepareRetry       (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgIsOriginalPhase    (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgWaitReselect       (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgIsPathActive       (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgAbortMsg           (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);

implement

type
	SynchParmsType = PACKED RECORD
		SynchExtend:s_byte;
		SynchLength:s_byte;
		SynchCode:s_byte;
		SynchPeriod:s_byte;
		SynchReqAck:s_byte;
	END;

const
	SynchParmsConst = SynchParmsType[
		SynchExtend:1,
		SynchLength:3,
		SynchCode:1,
		SynchPeriod:0,
		SynchReqAck:8];

type
	Xlate16vs32Type = array[0..1] of s_byte;
	PeriodXlateType = array[0..3] of Xlate16vs32Type;

const
	PeriodXlate = PeriodXlateType[
				 {16bit    32bit}
		Xlate16vs32Type [ 125,     62 ],
		Xlate16vs32Type [ 168,     93 ],
		Xlate16vs32Type [   0,    125 ],
		Xlate16vs32Type [   0,      0 ] ];


function GetSynchN(PeriodParm:integer):integer;
var
	i:integer;
begin
	{
	  compute from the transfer period parameter, the number to be placed in H/W.
	  The number to be placed in hardware is n-1 where

		  n = (4m/125)-1

	  The PeriodParm is m.

	  Because of rounding when m is generated, 4m needs to be modified to be an exact
	  multiple of 125.
	}
	i := PeriodParm * 4;
	GetSynchN := ((i + (i mod 125)) Div 125) - 2;
end;


{*************************************************************************
 *  msgWaitTimeOrFree
 *************************************************************************
 *
 * On Input:  Scsi Bus is busy.
 *
 *
 * Functional Description:
 *
 *      Wait for either the bus to free up or timeout.
 *
 *
 *
 * On Output:  BusFree, ScsiErr (InternalStatus set to ScsiTimeoutErr).
 *
 *
 *************************************************************************}
Procedure MsgWaitTimeOrFree     (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	with PtrSessionBlock^.InternalBlock.pScBlock^.PtrScsiChip^ do
	begin
		ResultCode := ORD(BusFree);
		if (ssts.init) then
		begin
			osdStartTimer(one_second);
			repeat
			until (not ssts.init) or (osdTimeExpired);

			if (ssts.init) then
			begin
				utlSetInternalErr(PtrSessionBlock, ScsiTimeOutErr);
				ResultCode := ORD(ScsiErr);
			end;
		end;
	end;
end;

{*************************************************************************
 *  MsgSelect
 *************************************************************************
 *
 * On Input: bus is in a free state.
 *
 *
 * Functional Description:
 *
 *      Select the device that this session wants to communicate with.
 *      Select with the attention line on to indicate that messages are
 *      supported and the MsgOut phase is desired upon successful selection.
 *
 *      Maintain a count of select attempts and err if count exceeds max.
 *      If select is unsuccessful, set result code to BusBusy.
 *
 *
 * On Output: WaitForIsr while waiting for a Select.  This is the point
 *      where overlap transfer begins and the state machine becomes interrupt
 *      driven.
 *      BusBusy for select fail.
 *      ScsiErr.
 *      NoEvent if sucessful.
 *
 *************************************************************************}
Procedure MsgSelect             (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
	i:integer;
begin
	with PtrSessionBlock^, InternalBlock.pScBlock^, PtrScsiCard^, PtrScsiChip^ do
	if not InternalBlock.ISRWaiting then
	begin
		i := osdBitLsL(1,Device);
		if (i = ord(bdid)) then
		begin
			utlSetInternalErr(PtrSessionBlock, ScsiAddressErr);
			ResultCode := ORD(ScsiErr);
		end
		else
		begin
			ResultCode := ORD(WaitForISR);
			InternalBlock.ISRWaiting := True;
			InternalBlock.ISRMask := cmd_complete + timeout;
			ints.ints := ints.ints;
			pctl.pctl := 0;
			data_regs.temp := chr(osdBitOr(ord(bdid), i));
			scmd.cmd := set_atn_cmd;
			data_regs.tch := chr(hex('0f'));
			data_regs.tcm := chr(hex('42'));
			data_regs.tcl := #4;
			scmd.cmd := select_cmd;
			sc_reg.ie := true;
		end;
	end
	else {ISR has occured}
	begin
		InternalBlock.ISRWaiting := false;
		sc_reg.ie := false;
		scmd.cmd := reset_atn_cmd;
		if InternalBlock.ISRError then
			ResultCode := ORD(ScsiErr)
		else
		begin
			if ints.time_out then
			begin
				if InternalBlock.SelectRetryCount >= SelectRetryMax then
				begin
					ResultCode := ORD(ScsiErr);
					utlSetInternalErr(PtrSessionBlock, SelectRetryErr);
					DeviceSynchParms[Device].SynchState := NoSynch;
				end
				else
				begin
					InternalBlock.SelectRetryCount :=
						InternalBlock.SelectRetryCount + 1;
					ResultCode := ORD(BusBusy);
				end;
			end
			else if ints.cmd_complete then
			begin
				{
				 wait for chip to settle down
				}
				if (ssts.action <> spc_init_idle) and
				   (ssts.action <> spc_init_wait_xfer) then
				begin
					osdStartTimer(one_second);
					repeat until (ssts.action = spc_init_idle) or
						     (ssts.action = spc_init_wait_xfer) or
						     osdTimeExpired;
					if (ssts.action <> spc_init_idle) and
					   (ssts.action <> spc_init_wait_xfer) then
					begin
						ResultCode := ORD(ScsiErr);
						utlSetInternalErr(PtrSessionBlock, ScsiTimeOutErr);
					end
					else
						ResultCode := ORD(NoEvent);
			       end
			       else
				       ResultCode := ORD(NoEvent);

				if (psns.t_phase <> msg_out_phase) and (psns.t_phase <> cmd_phase) then
				begin
					osdStartTimer(one_second);
					repeat until (psns.t_phase = msg_out_phase) or
						     (psns.t_phase = cmd_phase) or
						     (osdTimeExpired);
				end;
			end;
		end;
		ints.ints := ints.ints;
	end;
end;


{*************************************************************************
 *  MsgIdentifyMsg
 *************************************************************************
 *
 * On Input: Bus is in a MsgOut phase.
 *
 *
 * Functional Description:
 *
 *      Format and transmit an identify message to the device.
 *      The SUN (Seconday Unit Number) parameter is not supported.
 *      Range checking on the LUN value is performed here.
 *      The session parameter 'DoNotDisconnect' determines if disconnect
 *      will be allowed in this session.
 *
 *      This routine, before sending the identify message, must determine
 *      if synch parameters with this device should be discussed. If this is
 *      the case, maintain the ATN line set during the identify transfer and
 *      set the ResultCode to NeedSynchParms.
 *
 * On Output: NoEvent or NeedSynchParms if successful, otherwise ScsiErr.
 *
 *************************************************************************}
Procedure MsgIdentifyMsg        (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
type
	IdentifyMsgType = packed record case boolean of
			    true:( IdentifyBit:boolean;
				   AllowDisconnect:boolean;
				   rsrvd:0..7;
				   DevLun:0..7);
			   false:(b:s_byte);
			end;
Var
	IdentifyData:IdentifyMsgType;
	XferByte:s_byte;
	MaintainATN:Boolean;
begin
	{ Guaranteed to be in msgout phase when this routine is called. }

	With IdentifyData, PtrSessionBlock^, InternalBlock.pScBlock^, PtrScsiChip^ do
	if LUN < 8 then
	begin
		IdentifyBit := true;
		AllowDisconnect := not DoNotDisconnect;
		rsrvd := 0;
		DevLun := LUN;
		XferByte := b;

		MaintainATN := (DeviceSynchParms[Device].SynchState = NeedSynch);

		if hwiManXfer(PtrSessionBlock, XferByte, msg_out_phase, MaintainATN, false) then
		begin
			if MaintainATN then
				ResultCode := ORD(NeedSynchParms)
			else
				ResultCode := ORD(NoEvent);
		end
		else
		begin
			utlSetInternalErr(PtrSessionBlock, ScsiManXferErr);
			ResultCode := ORD(ScsiErr);
		end;
	end
	else
	begin
		utlSetInternalErr(PtrSessionBlock, ScsiAddressErr);
		ResultCode := ORD(ScsiErr);
	end;
end;

{*************************************************************************
 *  MsgInitSynchParms
 *************************************************************************
 *
 * On Input:  It has been determined that Synchronous transmission
 *            parameters must be set up.  The Bus should be in MsgOut phase.
 *            Agreement protocol used begins with initiator.
 *
 * Functional Description:
 *
 *      This routine handles all of the message passing required to agree
 *      with the target on synchronous transmission parameters.
 *
 *      The agreed upon synchronous parameters must be stored in the select
 *      code block.
 *
 *
 * On Output: NoEvent if successful.  ScsiErr Otherwise.
 *      Agreed upon synch. parms are in select code block.
 *
 *************************************************************************}
Procedure MsgInitSynchParms     (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
	SynchParms:SynchParmsType;
	SynchMsgBuf:SynchParmsType;
	DMA32BitIdx:integer;
	XferCode:s_byte;
	i:integer;
	pByte:PtrChar;
begin
	with PtrSessionBlock^, InternalBlock.pScBlock^,
	     PtrScsiCard^, PtrScsiChip^, DeviceSynchParms[Device] do
	begin
		ResultCode := ord(NoEvent);
		SynchParms := SynchParmsConst;
		DMA32bitIdx := ord( ((osdDMA32bit) and (not id_reg.d16)) );
		SynchParms.SynchPeriod := PeriodXlate[config_reg.sync_rate][DMA32bitIdx];
		if SynchParms.SynchPeriod = 0 then {current h/w can't synch}
		begin
			SynchState := NoSynch;

			{ turn off the attention line }
			XferCode := hex('08'); {noop message}
			if not hwiManXfer(PtrSessionBlock,XferCode,msg_out_phase,false,false) then
			begin
				ResultCode := ord(ScsiErr);
				utlSetInternalErr(PtrSessionBlock, ScsiManXferErr);
			end
			else
				ResultCode := ord(NoEvent);
		end
		else {h/w can synch, attempt to synch with device}
		begin
			pByte := addr(SynchParms);
			i := sizeof(SynchParms);
			XferCode := ord(pByte^);
			while (psns.t_phase = msg_out_phase) and
			      (i > 0) and
			      (ResultCode = ord(NoEvent)) do
			begin
				if (hwiManXfer(PtrSessionBlock, XferCode, msg_out_phase,
					       (i>1),false)) then
				begin
					pByte := addr(pByte^, 1);
					XferCode := ord(pByte^);
					i := i - 1;
				end
				else if (psns.t_phase = msg_out_phase) then
				begin
					SynchState := NoSynch;
					ResultCode := ord(ScsiErr);
					utlSetInternalErr(PtrSessionBlock, ScsiManXferErr);
				end;
				{
				  else if the phase has changed, then this is not an error.
				}
			end;

			if (ResultCode = ord(NoEvent)) then {synch sent out o.k., now get the target's reply}
			begin
				{get the message}
				pByte := addr(SynchMsgBuf);
				i := 0;
				while (psns.t_phase = msg_in_phase) and (ResultCode = ord(NoEvent)) do
				begin
					if hwiManXfer(PtrSessionBlock, XferCode, msg_in_phase,
						      (i<4),false) then
					begin
						pByte^ := chr(XferCode);
						pByte := addr(pByte^, 1);
						i := i + 1;
					end
					else if (psns.t_phase = msg_in_phase) then
					begin
						SynchState := NoSynch;
						ResultCode := ord(ScsiErr);
						utlSetInternalErr(PtrSessionBlock, ScsiManXferErr);
					end;
					{
					  else if the phase has changed, then this is not an error.
					}
				end;

				if (ResultCode = ord(NoEvent)) and (i > 0) then {target's reply is o.k.}
				begin
					if (i = 1) and
					   (SynchMsgBuf.SynchExtend = hex('07')) then {message reject}
					begin
						SynchState := NoSynch;
					end
					else if (i = 5) and
						(SynchMsgBuf.SynchExtend = hex('01')) and
						(SynchMsgBuf.SynchCode   = hex('01')) then
						{Synch parms message brought in}
					begin
						if (SynchMsgBuf.SynchReqAck = 0) then
						begin
							{target says do asynch}
							SynchState := NoSynch;
						end
						else {target negotiated synch parms}
						begin
							i := GetSynchN(SynchMsgBuf.SynchPeriod);
							if (SynchMsgBuf.SynchReqAck <=
								SynchParms.SynchReqAck) and
							   (SynchMsgBuf.SynchPeriod >=
								SynchParms.SynchPeriod) and
							   (i < 4) then
							begin
								{all parms are o.k.}
								SynchState := Synched;
								TMODvalue.sx := true;
								TMODvalue.maxto := SynchParms.SynchReqAck
										   MOD 8;
								TMODvalue.mintp := i;
								TMODvalue.pad := 0;
							end
							else {illegal parms}
							begin
								SynchState := NoSynch;
								MsgRejectMsg(PtrSessionBlock, ResultCode);
							end;
						end;
					end
					else
					begin
						{msg is not what is expected.}
						SynchState := NoSynch;
						MsgRejectMsg(PtrSessionBlock, ResultCode);
					end;
				end
				else if (i = 0) then {illegal phase}
				begin
					SynchState := NoSynch;
				end;
			end;
		end;
	end;
end;

{*************************************************************************
 *  MsgTargetSynchParms
 *************************************************************************
 *
 * On Input:  The target has requested that synchronous parms be set up.
 *            The target's parms are in the select code block.
 *            Agreement protocol used begins with target.
 *            Bus is in MsgOut phase.
 *
 * Functional Description:
 *
 *      This routine handles all of the message passing required to agree
 *      with the target on synchronous transmission parameters.
 *
 *      The agreed upon synchronous parameters must be stored in the select
 *      code block.
 *
 * On Output: NoEvent if successful.  ScsiErr Otherwise.
 *      Agreed upon synch. parms are in select code block.
 *
 *
 *************************************************************************}
Procedure MsgTargetSynchParms   (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
	SynchParms:SynchParmsType;
	DMA32BitIdx:integer;
	XferCode:s_byte;
	i:integer;
	N:integer;
	pByte:PtrChar;
begin
	with PtrSessionBlock^, InternalBlock, pScBlock^,
	     PtrScsiCard^, PtrScsiChip^, DeviceSynchParms[Device] do
	begin
		SynchState := NoSynch;
		ResultCode := ord(NoEvent);
		SynchParms := SynchParmsConst;
		DMA32bitIdx := ord( ((osdDMA32bit) and (not id_reg.d16)) );
		SynchParms.SynchPeriod := PeriodXlate[config_reg.sync_rate][DMA32bitIdx];
		if SynchParms.SynchPeriod = 0 then {current h/w can't synch}
		begin
			{ reject the request }
			SynchState := NoSynch;
			MsgRejectMsg(PtrSessionBlock, ResultCode);
		end
		else {h/w can synch, attempt to synch with device}
		begin
			{
			  Now evaluate and modify the targets req/ack parm as necessary.
			}
			if (TargetSynchReqAck = 0) then
			begin
				{target is really requesting asynchronous mode!}
				SynchState := NoSynch;
				MsgRejectMsg(PtrSessionBlock, ResultCode);
			end
			else if (TargetSynchReqAck < SynchParms.SynchReqAck) then
			begin
				{Conform to targets ReqAck request}
				SynchParms.SynchReqAck := TargetSynchReqAck;
			end;
			{
			   else, target will be asked to conform to our max by default.
			}


			{
			  Evaluate and modify the targets synch parm as necessary.
			}
			if (TargetSynchPeriod > SynchParms.SynchPeriod) then
			begin
				{
				  Target's request is less greater than our smallest period,
				  so go with it!.
				  First check if for validity!
				}
				N := GetSynchN(TargetSynchPeriod);
				if (N < 4) then {valid synch request}
				begin
					{Conform to targets Period request}
					SynchParms.SynchPeriod := TargetSynchPeriod;
				end
				else
				begin
					{target is requesting a period impossible for our H/W!}
					SynchState := NoSynch;
					MsgRejectMsg(PtrSessionBlock, ResultCode);
				end;
			end
			else
			begin
				{
				  ask target to conform to our smallest period.
				  This will occur naturally, however the var N needs to be recomputed.
				}
				N := GetSynchN(SynchParms.SynchPeriod);
			end;


			if (SynchState = NeedSynch) then {send back a valid response to target}
			begin
				ResultCode := ord(NoEvent);
				pByte := addr(SynchParms);
				i := sizeof(SynchParms);
				XferCode := ord(pByte^);
				while (psns.t_phase = msg_out_phase) and
				      (i > 0) and
				      (ResultCode = ord(NoEvent)) do
				begin
					if (hwiManXfer(PtrSessionBlock, XferCode,
						       msg_out_phase, (i>1),false)) then
					begin
						pByte := addr(pByte^, 1);
						XferCode := ord(pByte^);
						i := i - 1;
					end
					else
					begin
						SynchState := NoSynch;
						ResultCode := ord(ScsiErr);
						utlSetInternalErr(PtrSessionBlock, ScsiManXferErr);
					end;
				end;

				if (ResultCode = ord(NoEvent)) then {all synched!}
				begin
					{all parms are o.k., send back }
					SynchState := Synched;
					TMODvalue.sx := true;
					TMODvalue.maxto := SynchParms.SynchReqAck MOD 8;
					TMODvalue.mintp := N;
					TMODvalue.pad := 0;
				end;
			end;
			{ else a Reject Msg has already been sent. }
		end;
	end;
end;

{*************************************************************************
 *  MsgGetMsg
 *************************************************************************
 *
 * On Input: Bus is in the MsgIn phase.
 *
 *
 * Functional Description:
 *
 *      Acquire the incomming message.  If the message has data that
 *      needs to be stored for later reference, store in either the
 *      sessionblock or in the select code block.
 *
 *      If a parity error is detected, send the MessageParityError
 *      message and await a retry.
 *
 * On Output:  Message has been acquired and the message code is set in
 *      the ResultCode.  ScsiErr if an error has occured.
 *
 *************************************************************************}
Procedure MsgGetMsg             (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);

type
	XlateMsgType = array [0..12] of S_SHORT;
const
	XlateMsgCode = XlateMsgType[    ORD(CmdComplete),ORD(ScsiErr),ORD(SaveDataPtr),
					ORD(RestorePtrs),ORD(Disconnect),ORD(ScsiErr),
					ORD(ScsiErr),ORD(ScsiErr),ORD(ScsiErr),ORD(ScsiErr),
					ORD(LCC),ORD(LCCwFlag),ORD(ScsiErr)];
var
	Buf:packed array [ 1 .. 5 ] of char;
	XferCode:s_byte;
	i : integer;
begin

	with PtrSessionBlock^.InternalBlock, pScBlock^.PtrScsiChip^ do
	begin
		ResultCode := ord(NoEvent);

		{
		 read in the message.
		  - special note: if this is a synchronous parms message comming in, then
		    before the last byte is brought in, we have to request message out phase
		    for a reply.
		}
		i := 0;
		repeat
			if hwiManXfer(PtrSessionBlock, XferCode, msg_in_phase,
				      (i<4), ((i = 4) and (ord(buf[3]) = 1))) then
			begin
				i := i + 1;
				Buf[i] := chr(XferCode);
			end
			else if (psns.t_phase = msg_in_phase) then
			begin
				ResultCode := ord(ScsiErr);
				utlSetInternalErr(PtrSessionBlock, ScsiManXferErr);
			end;
			{
			  else if the phase has changed, then this is not an error.
			}
		until ( (ResultCode <> ord(NoEvent)) or
			(psns.t_phase <> msg_in_phase) or
			(Buf[1] <> #1)
		      );

		XferCode := ord(Buf[1]);
		if (i = 1) then {standard 1 byte message}
		begin
			if (XferCode div 128) <> 0 then   { Identify }
				ResultCode := ORD(Identify)
			else if XferCode > hex('0c') then
				Resultcode := ord(ScsiErr)
			else
				ResultCode := XlateMsgCode[XferCode];

			{
			  Wait for a possible phase change - there may be more
			  message bytes.  This is determined by the state of the REQ
			  line.
			}
			if (psns.t_phase = msg_in_phase) and (not psns.req) then
			begin
				osdStartTimer(one_second);
				repeat
				until ( (psns.t_phase <> msg_in_phase) or
					( (psns.t_phase = msg_in_phase) and (psns.req) ) or
					(osdTimeExpired)
				      );

				if (psns.t_phase = msg_in_phase) and (not psns.req) then
				begin
					utlSetInternalErr(PtrSessionBlock, ScsiPhaseErr);
					ResultCode := ord(ScsiErr);
				end;
			end;
		end
		else if (XferCode = hex('01')) {extended message} and
			(i = 5) and
			(ord(Buf[3]) = hex('01')) and {synchronous message}
			(psns.t_phase = msg_out_phase) then {guarantee msgout phase}
		begin
			ResultCode := ord(Synch);
			TargetSynchPeriod := ord(Buf[4]);
			TargetSynchReqAck := ord(Buf[5]);
		end
		else {unknown message}
			ResultCode := ord(ScsiErr);


		if ResultCode = ORD(ScsiErr) then
			utlSetInternalErr(PtrSessionBlock, ScsiBadMsg);
	end;
end;

{*************************************************************************
 *  MsgRejectMsg
 *************************************************************************
 *
 * On Input: Bus can be in any phase.
 *
 *
 * Functional Description:
 *
 *      Acquire the MsgOut phase (hwiGetMsgOut)
 *      Format and send a MessageReject Message.
 *
 * On Output:  NoEvent if successful. ScsiErr otherwise.
 *
 *************************************************************************}
Procedure MsgRejectMsg          (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
  Xfer_Code : s_byte;
begin
    hwiGetMsgOut(PtrSessionBlock,ResultCode);
    if ResultCode = ORD(MsgOut) then begin
	xfer_code:=hex('07');
	if hwiManXfer(PtrSessionBlock,xfer_code,msg_out_phase,false,false) then
	  ResultCode := ORD(NoEvent)
	else
	begin
	  ResultCode := ORD(ScsiErr);
	  utlSetInternalErr(PtrSessionBlock, ScsiManXferErr);
	end;
    end;
end;


{*************************************************************************
 *  MsgSetXfer
 *************************************************************************
 *
 * On Input: Bus is in either CmdPhase or DataPhase.
 *
 *
 * Functional Description:
 *
 *      Set up the XferBlock which is located in the InternalBlock of the
 *      SessionBlock.
 *
 *      The XferPhase is the current bus phase.
 *      The XferRetryCount is set to 0.
 *      The XferBlock is set up according to the bus phase:
 *
 *              CmdPhase:       Use the CmdPtr and CmdLen.
 *                              DoDMA is always false.
 *                              Set up TMOD: if the device has synched, used TMODvalue.
 *
 *              DataIn or DataOut: Use BufIn or BufOut.
 *
 *      Set XferSavedDataPointer and XferSavedDateLength equal to BufPtr and BufLen.
 *
 * On Output: NoEvent.  XferBlock has been set up for a transfer.
 *            ScsiErr.  Illegal value in the XferBlock.
 *
 *************************************************************************}
Procedure MsgSetXfer            (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
	locTMOD:tmod_type;
begin
	ResultCode := ORD(NoEvent);
	With PtrSessionBlock^, InternalBlock.XferBlock, InternalBlock.pScBlock^, DeviceSynchParms[Device] do
	begin
		XferPhase := PtrScsiChip^.psns.t_phase;

		XferRetryCount := 0;

		case XferPhase of
			cmd_phase:
				begin
					XferBufBlock.BufPtr := CmdPtr;
					XferBufBlock.BufLen := CmdLen;
					XferBufBlock.DoDMA := false;

					{
					  Before entering data phase, the chip must see the
					  transfer method.
					}
					if (SynchState = Synched) then
						locTMOD.tmod := TMODvalue.tmod
					else
						locTMOD.tmod := hex('00');
					PtrScsiChip^.tmod.tmod := locTMOD.tmod;
				end;
			data_out_phase:
				XferBufBlock := BufOut;
			data_in_phase:
				XferBufBlock := BufIn;
			otherwise
				begin
					utlSetInternalErr(PtrSessionBlock, ScsiPhaseErr);
					ResultCode := ORD(ScsiErr);
				end;
		end;

		if (XferBufBlock.BufPtr = Nil) or
		   (XferBufBlock.BufLen <= 0) or
		   (XferBufBlock.BufLen > hex('00ffffff')) then
		begin
			utlSetInternalErr(PtrSessionBlock, ScsiXferParmErr);
			ResultCode := ORD(ScsiErr);
		end
		else if (odd(ord(XferBufBlock.BufPtr))) or (odd(XferBufBlock.BufLen)) then
			XferBufBlock.DoDMA := FALSE;

		if ResultCode = ORD(NoEvent) then {no errors have occured so far.}
		begin
			XferSavedDataPointer :=
						XferBufBlock.BufPtr;
			XferSavedDataLength  :=
						XferBufBlock.BufLen;
		end;
	end;
end;



{*************************************************************************
 *  MsgDoXfer
 *************************************************************************
 *
 * On Input: Bus is in correct phase and the XferBlock has been set up.
 *
 *
 * Functional Description:
 *
 *      Sets up the hardware for a transfer and controls the transfer.
 *      If DoDMA, sets up DMA and waits for an interrupt.
 *
 *      If an error occurs, this routine handles the error and waits until
 *      the bus is in message out phase (calls hwiGetMsgOut).
 *
 *      If the xfer is stopped by the target, this is o.k., however this
 *      routine must see that any untransmitted data that is in the chip/DMA
 *      buffers are cleared out and that the buffer pointer and length is
 *      set to the next byte to be transferred.
 *
 * On Output:   ScsiErr if an unrecoverable hardware error occurs.
 *              MsgOut if error occured and bus successfuly moved to MsgOut phase.
 *              NoEvent otherwise.
 *
 *************************************************************************}
Procedure MsgDoXfer             (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	With PtrSessionBlock^, InternalBlock, XferBlock, pScBlock^, PtrScsiChip^ do
	begin
		ResultCode := ord(NoEvent);

		{
		  if not waiting for an ISR, then starting or renewing the
		  transfer defined in the XferBlock.
		}
		if NOT ISRWaiting then
		begin
			{
			  Is DMA Requested on this buffer block?
			  if so, attempt to get a DMA channel.
			  If one is unavailable, convert this xfer to
			  a programmatic xfer.
			}
			if XferBlock.XferBufBlock.DoDMA then
			begin
				DMAChannel := osdGetDMAChannel(SelectCode);
				if DMAChannel = -1 then
					XferBufBlock.DoDMA:=false;
			end;

			{
			  If a DMA channel has been acquired, then start the DMA.
			  Otherwise, this is a programmatic transfer.
			}
			if XferBlock.XferBufBlock.DoDMA then
			begin
				DMAInProgress := TRUE;
				if (not hwiStartDMAXfer(PtrSessionBlock)) then
					ResultCode := ord(ScsiErr)
				else
					ResultCode := ord(WaitForISR);
			end
			else {programmatic xfer}
			begin
				if (not hwiProgXfer(PtrSessionBlock)) then
					ResultCode := Ord(ScsiErr);
			end;
		end
		else {chip interrupt after DMA}
		begin
			if (not hwiEndDMAXfer(PtrSessionBlock, ResultCode)) then
			begin
				{
				  DMAXfer has completed, but needs to be restarted to
				  complete the overall transfer
				}
				DMAInProgress := TRUE;
				if (not hwiStartDMAXfer(PtrSessionBlock)) then
					ResultCode := ord(ScsiErr)
				else
					ResultCode := ord(WaitForISR);
			end
			else
			begin
				{
				  DMAXfer has completed, ResultCode contains the state machine
				  result.
				}
				osdReleaseDMAChannel(SelectCode);
				DMAInProgress := FALSE;
			end;
		end;


		if (ResultCode = ord(NoEvent)) and (psns.t_phase = XferPhase) then
		begin
			{
			  wait for the peripheral to change to the next phase

			  if the driver has requested the correct amount of data,
			  but the target is expecting more data (happens if user
			  gives incorrect parms) then could wait forever and
			  the phase will not change.  But how long to wait?  What
			  if the command were a format command which can take
			  30 minutes or longer!  The phase still wouldn't change
			  until the disc has formatted.  There is nothing that
			  can be done in this case - PWS will hang.
			}
			repeat until (psns.t_phase <> XferPhase) or (ints.ints <> 0);
			if (psns.t_phase = XferPhase) then
			begin
				utlSetInternalErr(PtrSessionBlock, ScsiXferErr);
				ResultCode := Ord(ScsiErr);
			end;
		end;
	end; {with}
end;

{*************************************************************************
 *  MsgSawErrMsg
 *************************************************************************
 *
 * On Input:    Bus is in MsgOut phase and a transmission error has been
 *              detected during a Command or Data phase.
 *
 * Functional Description:
 *
 *      Send a InitiatorDetectedError message to the target.
 *      Do not raise the ATN line during the transfer.
 *
 *
 * On Output:   NoEvent if message successfully sent.
 *              ScsiErr otherwise.
 *
 *************************************************************************}
Procedure MsgSawErrMsg          (PtrSessionBlock:PtrSessionBlockType;
				 Var ResultCode:S_SHORT);
var
  xfer_code : s_byte;

begin
	with PtrSessionBlock^.InternalBlock.XferBlock do
	begin
	    XferRetryCount := XferRetryCount + 1;
	    if XferRetryCount > XferRetryMax then
	    begin
		ResultCode := ORD(ScsiErr);
		utlSetInternalErr(PtrSessionBlock, XferRetryErr);
	    end
	    else
	    begin
		xfer_code:=hex('05');
		if hwiManXfer(PtrSessionBlock,xfer_code,msg_out_phase,false,false) then
		  ResultCode := ORD(NoEvent)
		else
		begin
		  ResultCode := ORD(ScsiErr);
		  utlSetInternalErr(PtrSessionBlock, ScsiXferErr);
		end;
	    end;
	 end;
 end;

{*************************************************************************
 *  MsgPrepareDisc
 *************************************************************************
 *
 * On Input:    A Save Data Pointers message has just been received.
 *              This means that the target is either getting ready to
 *              disconnect or wants to ensure that the transmission done
 *              so far is not lost if an error occurs latter on.
 *
 * Functional Description:
 *
 *      Copy the current buffer pointer and length to the save pointer
 *      and length.
 *
 * On Output:   NoEvent
 *
 *
 *************************************************************************}
Procedure MsgPrepareDisc        (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	With PtrSessionBlock^.InternalBlock.XferBlock do
	begin
		XferSavedDataPointer := XferBufBlock.BufPtr;
		XferSavedDataLength := XferBufBlock.BufLen;
	end;
	ResultCode := Ord(NoEvent);
end;

{*************************************************************************
 *  MsgSetDisc
 *************************************************************************
 *
 * On Input:    A disconnect message has been received.
 *
 *
 * Functional Description:
 *
 *      wait for disconnect to happen.
 *      turn off disconnect interrupt
 *      Set session state to suspended.
 *
 * On Output:   BusFree if disconnect occurs, ScsiErr otherwise.
 *
 *
 *************************************************************************}
Procedure MsgSetDisc            (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
label 1;
begin
	with PtrSessionBlock^, InternalBlock.pScBlock^.PtrScsiChip^ do
	begin
		if not ints.disconnected then
		begin
			osdStartTimer(one_second);
			repeat until (ints.disconnected) or osdTimeExpired;
			if not ints.disconnected then
			begin
				utlSetInternalErr(PtrSessionBlock, ScsiTimeOutErr);
				ResultCode := ORD(ScsiErr);
				goto 1;
			end;
		end;
		ints.ints := disconnected; { turn off disconnected interrupt }
		SessionState := SessionSuspended;
		ResultCode := ORD(BusFree);
	end;
	1:
end;

{*************************************************************************
 *  MsgPrepareRetry
 *************************************************************************
 *
 * On Input:    A RestorePtrs messgae has just been recieved.
 *              This means that the target is recovering from an error
 *              or is returnning from a disconnect.  If recovering from
 *              an error, then when the next transmission occurs, the
 *              transmission will begin from the last time the pointers
 *              were saved (beginning of transfer or save data pointers msg).
 *
 * Functional Description:
 *
 *      Copy the save pointer and length to the buffer pointer and length.
 *
 * On Output:   NoEvent
 *
 *
 *************************************************************************}
Procedure MsgPrepareRetry       (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	With PtrSessionBlock^, InternalBlock, XferBlock, pScBlock^,
	     PtrScsiChip^, DeviceSynchParms[Device] do
	begin
		XferBufBlock.BufPtr := XferSavedDataPointer;
		XferBufBlock.BufLen := XferSavedDataLength;
	end;
	ResultCode := Ord(NoEvent);
end;

{*************************************************************************
 *  MsgIsOriginalPhase
 *************************************************************************
 *
 * On Input: XferBlock in SessionBlock contains original h/w phase.
 *
 *
 * Functional Description:
 *
 *      obtain the current bus phase (EventType) and compare with what is in the
 *      Xfer block.
 *
 *
 *
 *
 * On Output:  IsOriginalPhase if match is good, otherwise EventType bus phase.
 *
 *
 *************************************************************************}
Procedure MsgIsOriginalPhase    (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	hwiGetPhase(PtrSessionBlock, ResultCode);
	with PtrSessionBlock^.InternalBlock.XferBlock,
	     PtrSessionBlock^.InternalBlock.pScBlock^.PtrScsiChip^ do
	begin
		if (ResultCode = ORD(CmdPhase)) and (XferPhase = cmd_phase) then
			ResultCode := ORD(OriginalPhase)
		else if (ResultCode = ORD(DataPhase)) then
		begin
			if (psns.io) then
			begin
				if (XferPhase = data_in_phase) then
					ResultCode := ORD(OriginalPhase);
			end
			else
			begin
				if (XferPhase = data_out_phase) then
					ResultCode := ORD(OriginalPhase);
			end;
		end;
	end;
end;

{*************************************************************************
 *  MsgWaitReselect
 *************************************************************************
 *
 * On Input:    The bus is in a bus free state.  The target must reselect
 *              this session.  It is possible that the reselect has
 *              already occured.
 *
 * Functional Description:
 *
 *      Set up for an interrupt to occur when target reselects this session.
 *      When reselected, set the session state to running, and resets the current transfer.
 *
 * On Output:   ScsiErr if timeout, otherwise NoEvent.
 *
 *************************************************************************}
Procedure MsgWaitReselect       (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
	xfer_code : s_byte;
	locTMOD:tmod_type;
begin
	with PtrSessionBlock^, InternalBlock, pScBlock^,
	     PtrScsiCard^, PtrScsiChip^, DeviceSynchParms[Device] do
	if (not ISRWaiting) and (not ints.reselected) then
	begin
		ResultCode := ORD(WaitForISR);
		ISRWaiting := True;
		ISRMask := Reselected;
		sc_reg.ie := true;
	end
	else {ISR has occured or Reselected has already happened}
	begin
		sc_reg.ie := false;
		ints.ints := ints.ints;
		ISRWaiting := false;
		SessionState := SessionRunning;
		{
		  tmod value must be set up before chip sees data in/out transfer
		  phase.
		}
		if (SynchState = Synched) then
			locTMOD.tmod := TMODvalue.tmod
		else
			locTMOD.tmod := hex('00');
		tmod.tmod := locTMOD.tmod;

		ResultCode := ord(MsgIn);
		if ISRError then
			ResultCode := ORD(ScsiErr)
		else if (psns.t_phase <> msg_in_phase) then
		begin
			osdStartTimer(one_second);
			repeat until ( (psns.t_phase = msg_in_phase) or (osdTimeExpired) );

			if (psns.t_phase <> msg_in_phase) then
			begin
				utlSetInternalErr(PtrSessionBlock, ScsiPhaseErr);
				ResultCode := ord(ScsiErr);
			end;
		end;
	 end;
end;

{*************************************************************************
 *  MsgIsPathActive
 *************************************************************************
 *
 * On Input:    Nothing expected
 *
 *
 * Functional Description:
 *
 *      Checks if the session is currently talking to a device.
 *      If the session is running and the chip is connected, then the
 *      path is active.
 *
 *      If the path is not active, return if the bus is busy or free.
 *
 * On Output:   NoEvent if path is active, BusFree or BusBusy otherwise.
 *
 *************************************************************************}
Procedure MsgIsPathActive       (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	with PtrSessionBlock^, InternalBlock.pScBlock^.PtrScsiChip^ do
	begin
		if (SessionState = SessionRunning) and (ssts.init) then
			ResultCode := ORD(NoEvent)
		else if psns.bsy then
			ResultCode := ORD(BusBusy)
		else
			ResultCode := ORD(BusFree);
	end;
end;

{*************************************************************************
 *  MsgAbortMsg
 *************************************************************************
 *
 * On Input: bus is in message out phase.
 *
 *
 * Functional Description:
 *
 *      Transfer an AbortMessage to the target.
 *
 *
 *
 *
 * On Output:   ScsiErr or NoEvent.
 *
 *
 *************************************************************************}
Procedure MsgAbortMsg           (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
   xfer_code : s_byte;

begin
    xfer_code:=hex('06');
    if hwiManXfer(PtrSessionBlock,xfer_code,msg_out_phase,false,false) then
      ResultCode := ORD(NoEvent)
    else
    begin
      ResultCode := ORD(ScsiErr);
      utlSetInternalErr(PtrSessionBlock, ScsiXferErr);
    end;
end;

end;

@


56.3
log
@
pws2rcs automatic delta on Wed Jan 27 11:57:27 MST 1993
@
text
@d1 1285
@


56.2
log
@Changed state from NeedSynch to NoSynch to fix bug with 400MB drives - CFB
@
text
@a0 1285
{
	SCSI Messages Module.

	Contains the SCSI driver message level interface.
	This includes all actions as defined by the network level, that
	is to: 1) set up a path between the host and target,
	       2) maintain that path (handle comminication, transmission
		  errors, disconnect/reconnect)
	       3) terminate the path.

	The transport (command) level interacts with the network (message)
	level to set up paths, handle transmission errors, etc.

	The SCSI Driver is state machine driven.
	The State Machine actually makes all of the calls, and the state
	tables enforce the division of labor (commands/messages) as defined
	above.
}
module MESSAGES;

import SCSI_DEFS, SCSI_UTILS, HWI_UTILS, OSD_LL;

export
	Procedure MsgWaitTimeOrFree     (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgSelect             (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgIdentifyMsg        (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgInitSynchParms     (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgTargetSynchParms   (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgGetMsg             (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgRejectMsg          (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgSetXfer            (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgDoXfer             (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgSawErrMsg          (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgPrepareDisc        (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgSetDisc            (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgPrepareRetry       (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgIsOriginalPhase    (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgWaitReselect       (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgIsPathActive       (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure MsgAbortMsg           (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);

implement

type
	SynchParmsType = PACKED RECORD
		SynchExtend:s_byte;
		SynchLength:s_byte;
		SynchCode:s_byte;
		SynchPeriod:s_byte;
		SynchReqAck:s_byte;
	END;

const
	SynchParmsConst = SynchParmsType[
		SynchExtend:1,
		SynchLength:3,
		SynchCode:1,
		SynchPeriod:0,
		SynchReqAck:8];

type
	Xlate16vs32Type = array[0..1] of s_byte;
	PeriodXlateType = array[0..3] of Xlate16vs32Type;

const
	PeriodXlate = PeriodXlateType[
				 {16bit    32bit}
		Xlate16vs32Type [ 125,     62 ],
		Xlate16vs32Type [ 168,     93 ],
		Xlate16vs32Type [   0,    125 ],
		Xlate16vs32Type [   0,      0 ] ];


function GetSynchN(PeriodParm:integer):integer;
var
	i:integer;
begin
	{
	  compute from the transfer period parameter, the number to be placed in H/W.
	  The number to be placed in hardware is n-1 where

		  n = (4m/125)-1

	  The PeriodParm is m.

	  Because of rounding when m is generated, 4m needs to be modified to be an exact
	  multiple of 125.
	}
	i := PeriodParm * 4;
	GetSynchN := ((i + (i mod 125)) Div 125) - 2;
end;


{*************************************************************************
 *  msgWaitTimeOrFree
 *************************************************************************
 *
 * On Input:  Scsi Bus is busy.
 *
 *
 * Functional Description:
 *
 *      Wait for either the bus to free up or timeout.
 *
 *
 *
 * On Output:  BusFree, ScsiErr (InternalStatus set to ScsiTimeoutErr).
 *
 *
 *************************************************************************}
Procedure MsgWaitTimeOrFree     (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	with PtrSessionBlock^.InternalBlock.pScBlock^.PtrScsiChip^ do
	begin
		ResultCode := ORD(BusFree);
		if (ssts.init) then
		begin
			osdStartTimer(one_second);
			repeat
			until (not ssts.init) or (osdTimeExpired);

			if (ssts.init) then
			begin
				utlSetInternalErr(PtrSessionBlock, ScsiTimeOutErr);
				ResultCode := ORD(ScsiErr);
			end;
		end;
	end;
end;

{*************************************************************************
 *  MsgSelect
 *************************************************************************
 *
 * On Input: bus is in a free state.
 *
 *
 * Functional Description:
 *
 *      Select the device that this session wants to communicate with.
 *      Select with the attention line on to indicate that messages are
 *      supported and the MsgOut phase is desired upon successful selection.
 *
 *      Maintain a count of select attempts and err if count exceeds max.
 *      If select is unsuccessful, set result code to BusBusy.
 *
 *
 * On Output: WaitForIsr while waiting for a Select.  This is the point
 *      where overlap transfer begins and the state machine becomes interrupt
 *      driven.
 *      BusBusy for select fail.
 *      ScsiErr.
 *      NoEvent if sucessful.
 *
 *************************************************************************}
Procedure MsgSelect             (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
	i:integer;
begin
	with PtrSessionBlock^, InternalBlock.pScBlock^, PtrScsiCard^, PtrScsiChip^ do
	if not InternalBlock.ISRWaiting then
	begin
		i := osdBitLsL(1,Device);
		if (i = ord(bdid)) then
		begin
			utlSetInternalErr(PtrSessionBlock, ScsiAddressErr);
			ResultCode := ORD(ScsiErr);
		end
		else
		begin
			ResultCode := ORD(WaitForISR);
			InternalBlock.ISRWaiting := True;
			InternalBlock.ISRMask := cmd_complete + timeout;
			ints.ints := ints.ints;
			pctl.pctl := 0;
			data_regs.temp := chr(osdBitOr(ord(bdid), i));
			scmd.cmd := set_atn_cmd;
			data_regs.tch := chr(hex('0f'));
			data_regs.tcm := chr(hex('42'));
			data_regs.tcl := #4;
			scmd.cmd := select_cmd;
			sc_reg.ie := true;
		end;
	end
	else {ISR has occured}
	begin
		InternalBlock.ISRWaiting := false;
		sc_reg.ie := false;
		scmd.cmd := reset_atn_cmd;
		if InternalBlock.ISRError then
			ResultCode := ORD(ScsiErr)
		else
		begin
			if ints.time_out then
			begin
				if InternalBlock.SelectRetryCount >= SelectRetryMax then
				begin
					ResultCode := ORD(ScsiErr);
					utlSetInternalErr(PtrSessionBlock, SelectRetryErr);
					DeviceSynchParms[Device].SynchState := NoSynch;
				end
				else
				begin
					InternalBlock.SelectRetryCount :=
						InternalBlock.SelectRetryCount + 1;
					ResultCode := ORD(BusBusy);
				end;
			end
			else if ints.cmd_complete then
			begin
				{
				 wait for chip to settle down
				}
				if (ssts.action <> spc_init_idle) and
				   (ssts.action <> spc_init_wait_xfer) then
				begin
					osdStartTimer(one_second);
					repeat until (ssts.action = spc_init_idle) or
						     (ssts.action = spc_init_wait_xfer) or
						     osdTimeExpired;
					if (ssts.action <> spc_init_idle) and
					   (ssts.action <> spc_init_wait_xfer) then
					begin
						ResultCode := ORD(ScsiErr);
						utlSetInternalErr(PtrSessionBlock, ScsiTimeOutErr);
					end
					else
						ResultCode := ORD(NoEvent);
			       end
			       else
				       ResultCode := ORD(NoEvent);

				if (psns.t_phase <> msg_out_phase) and (psns.t_phase <> cmd_phase) then
				begin
					osdStartTimer(one_second);
					repeat until (psns.t_phase = msg_out_phase) or
						     (psns.t_phase = cmd_phase) or
						     (osdTimeExpired);
				end;
			end;
		end;
		ints.ints := ints.ints;
	end;
end;


{*************************************************************************
 *  MsgIdentifyMsg
 *************************************************************************
 *
 * On Input: Bus is in a MsgOut phase.
 *
 *
 * Functional Description:
 *
 *      Format and transmit an identify message to the device.
 *      The SUN (Seconday Unit Number) parameter is not supported.
 *      Range checking on the LUN value is performed here.
 *      The session parameter 'DoNotDisconnect' determines if disconnect
 *      will be allowed in this session.
 *
 *      This routine, before sending the identify message, must determine
 *      if synch parameters with this device should be discussed. If this is
 *      the case, maintain the ATN line set during the identify transfer and
 *      set the ResultCode to NeedSynchParms.
 *
 * On Output: NoEvent or NeedSynchParms if successful, otherwise ScsiErr.
 *
 *************************************************************************}
Procedure MsgIdentifyMsg        (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
type
	IdentifyMsgType = packed record case boolean of
			    true:( IdentifyBit:boolean;
				   AllowDisconnect:boolean;
				   rsrvd:0..7;
				   DevLun:0..7);
			   false:(b:s_byte);
			end;
Var
	IdentifyData:IdentifyMsgType;
	XferByte:s_byte;
	MaintainATN:Boolean;
begin
	{ Guaranteed to be in msgout phase when this routine is called. }

	With IdentifyData, PtrSessionBlock^, InternalBlock.pScBlock^, PtrScsiChip^ do
	if LUN < 8 then
	begin
		IdentifyBit := true;
		AllowDisconnect := not DoNotDisconnect;
		rsrvd := 0;
		DevLun := LUN;
		XferByte := b;

		MaintainATN := (DeviceSynchParms[Device].SynchState = NeedSynch);

		if hwiManXfer(PtrSessionBlock, XferByte, msg_out_phase, MaintainATN, false) then
		begin
			if MaintainATN then
				ResultCode := ORD(NeedSynchParms)
			else
				ResultCode := ORD(NoEvent);
		end
		else
		begin
			utlSetInternalErr(PtrSessionBlock, ScsiManXferErr);
			ResultCode := ORD(ScsiErr);
		end;
	end
	else
	begin
		utlSetInternalErr(PtrSessionBlock, ScsiAddressErr);
		ResultCode := ORD(ScsiErr);
	end;
end;

{*************************************************************************
 *  MsgInitSynchParms
 *************************************************************************
 *
 * On Input:  It has been determined that Synchronous transmission
 *            parameters must be set up.  The Bus should be in MsgOut phase.
 *            Agreement protocol used begins with initiator.
 *
 * Functional Description:
 *
 *      This routine handles all of the message passing required to agree
 *      with the target on synchronous transmission parameters.
 *
 *      The agreed upon synchronous parameters must be stored in the select
 *      code block.
 *
 *
 * On Output: NoEvent if successful.  ScsiErr Otherwise.
 *      Agreed upon synch. parms are in select code block.
 *
 *************************************************************************}
Procedure MsgInitSynchParms     (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
	SynchParms:SynchParmsType;
	SynchMsgBuf:SynchParmsType;
	DMA32BitIdx:integer;
	XferCode:s_byte;
	i:integer;
	pByte:PtrChar;
begin
	with PtrSessionBlock^, InternalBlock.pScBlock^,
	     PtrScsiCard^, PtrScsiChip^, DeviceSynchParms[Device] do
	begin
		ResultCode := ord(NoEvent);
		SynchParms := SynchParmsConst;
		DMA32bitIdx := ord( ((osdDMA32bit) and (not id_reg.d16)) );
		SynchParms.SynchPeriod := PeriodXlate[config_reg.sync_rate][DMA32bitIdx];
		if SynchParms.SynchPeriod = 0 then {current h/w can't synch}
		begin
			SynchState := NoSynch;

			{ turn off the attention line }
			XferCode := hex('08'); {noop message}
			if not hwiManXfer(PtrSessionBlock,XferCode,msg_out_phase,false,false) then
			begin
				ResultCode := ord(ScsiErr);
				utlSetInternalErr(PtrSessionBlock, ScsiManXferErr);
			end
			else
				ResultCode := ord(NoEvent);
		end
		else {h/w can synch, attempt to synch with device}
		begin
			pByte := addr(SynchParms);
			i := sizeof(SynchParms);
			XferCode := ord(pByte^);
			while (psns.t_phase = msg_out_phase) and
			      (i > 0) and
			      (ResultCode = ord(NoEvent)) do
			begin
				if (hwiManXfer(PtrSessionBlock, XferCode, msg_out_phase,
					       (i>1),false)) then
				begin
					pByte := addr(pByte^, 1);
					XferCode := ord(pByte^);
					i := i - 1;
				end
				else if (psns.t_phase = msg_out_phase) then
				begin
					SynchState := NoSynch;
					ResultCode := ord(ScsiErr);
					utlSetInternalErr(PtrSessionBlock, ScsiManXferErr);
				end;
				{
				  else if the phase has changed, then this is not an error.
				}
			end;

			if (ResultCode = ord(NoEvent)) then {synch sent out o.k., now get the target's reply}
			begin
				{get the message}
				pByte := addr(SynchMsgBuf);
				i := 0;
				while (psns.t_phase = msg_in_phase) and (ResultCode = ord(NoEvent)) do
				begin
					if hwiManXfer(PtrSessionBlock, XferCode, msg_in_phase,
						      (i<4),false) then
					begin
						pByte^ := chr(XferCode);
						pByte := addr(pByte^, 1);
						i := i + 1;
					end
					else if (psns.t_phase = msg_in_phase) then
					begin
						SynchState := NoSynch;
						ResultCode := ord(ScsiErr);
						utlSetInternalErr(PtrSessionBlock, ScsiManXferErr);
					end;
					{
					  else if the phase has changed, then this is not an error.
					}
				end;

				if (ResultCode = ord(NoEvent)) and (i > 0) then {target's reply is o.k.}
				begin
					if (i = 1) and
					   (SynchMsgBuf.SynchExtend = hex('07')) then {message reject}
					begin
						SynchState := NoSynch;
					end
					else if (i = 5) and
						(SynchMsgBuf.SynchExtend = hex('01')) and
						(SynchMsgBuf.SynchCode   = hex('01')) then
						{Synch parms message brought in}
					begin
						if (SynchMsgBuf.SynchReqAck = 0) then
						begin
							{target says do asynch}
							SynchState := NoSynch;
						end
						else {target negotiated synch parms}
						begin
							i := GetSynchN(SynchMsgBuf.SynchPeriod);
							if (SynchMsgBuf.SynchReqAck <=
								SynchParms.SynchReqAck) and
							   (SynchMsgBuf.SynchPeriod >=
								SynchParms.SynchPeriod) and
							   (i < 4) then
							begin
								{all parms are o.k.}
								SynchState := Synched;
								TMODvalue.sx := true;
								TMODvalue.maxto := SynchParms.SynchReqAck
										   MOD 8;
								TMODvalue.mintp := i;
								TMODvalue.pad := 0;
							end
							else {illegal parms}
							begin
								SynchState := NoSynch;
								MsgRejectMsg(PtrSessionBlock, ResultCode);
							end;
						end;
					end
					else
					begin
						{msg is not what is expected.}
						SynchState := NoSynch;
						MsgRejectMsg(PtrSessionBlock, ResultCode);
					end;
				end
				else if (i = 0) then {illegal phase}
				begin
					SynchState := NoSynch;
				end;
			end;
		end;
	end;
end;

{*************************************************************************
 *  MsgTargetSynchParms
 *************************************************************************
 *
 * On Input:  The target has requested that synchronous parms be set up.
 *            The target's parms are in the select code block.
 *            Agreement protocol used begins with target.
 *            Bus is in MsgOut phase.
 *
 * Functional Description:
 *
 *      This routine handles all of the message passing required to agree
 *      with the target on synchronous transmission parameters.
 *
 *      The agreed upon synchronous parameters must be stored in the select
 *      code block.
 *
 * On Output: NoEvent if successful.  ScsiErr Otherwise.
 *      Agreed upon synch. parms are in select code block.
 *
 *
 *************************************************************************}
Procedure MsgTargetSynchParms   (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
	SynchParms:SynchParmsType;
	DMA32BitIdx:integer;
	XferCode:s_byte;
	i:integer;
	N:integer;
	pByte:PtrChar;
begin
	with PtrSessionBlock^, InternalBlock, pScBlock^,
	     PtrScsiCard^, PtrScsiChip^, DeviceSynchParms[Device] do
	begin
		SynchState := NoSynch;
		ResultCode := ord(NoEvent);
		SynchParms := SynchParmsConst;
		DMA32bitIdx := ord( ((osdDMA32bit) and (not id_reg.d16)) );
		SynchParms.SynchPeriod := PeriodXlate[config_reg.sync_rate][DMA32bitIdx];
		if SynchParms.SynchPeriod = 0 then {current h/w can't synch}
		begin
			{ reject the request }
			SynchState := NoSynch;
			MsgRejectMsg(PtrSessionBlock, ResultCode);
		end
		else {h/w can synch, attempt to synch with device}
		begin
			{
			  Now evaluate and modify the targets req/ack parm as necessary.
			}
			if (TargetSynchReqAck = 0) then
			begin
				{target is really requesting asynchronous mode!}
				SynchState := NoSynch;
				MsgRejectMsg(PtrSessionBlock, ResultCode);
			end
			else if (TargetSynchReqAck < SynchParms.SynchReqAck) then
			begin
				{Conform to targets ReqAck request}
				SynchParms.SynchReqAck := TargetSynchReqAck;
			end;
			{
			   else, target will be asked to conform to our max by default.
			}


			{
			  Evaluate and modify the targets synch parm as necessary.
			}
			if (TargetSynchPeriod > SynchParms.SynchPeriod) then
			begin
				{
				  Target's request is less greater than our smallest period,
				  so go with it!.
				  First check if for validity!
				}
				N := GetSynchN(TargetSynchPeriod);
				if (N < 4) then {valid synch request}
				begin
					{Conform to targets Period request}
					SynchParms.SynchPeriod := TargetSynchPeriod;
				end
				else
				begin
					{target is requesting a period impossible for our H/W!}
					SynchState := NoSynch;
					MsgRejectMsg(PtrSessionBlock, ResultCode);
				end;
			end
			else
			begin
				{
				  ask target to conform to our smallest period.
				  This will occur naturally, however the var N needs to be recomputed.
				}
				N := GetSynchN(SynchParms.SynchPeriod);
			end;


			if (SynchState = NeedSynch) then {send back a valid response to target}
			begin
				ResultCode := ord(NoEvent);
				pByte := addr(SynchParms);
				i := sizeof(SynchParms);
				XferCode := ord(pByte^);
				while (psns.t_phase = msg_out_phase) and
				      (i > 0) and
				      (ResultCode = ord(NoEvent)) do
				begin
					if (hwiManXfer(PtrSessionBlock, XferCode,
						       msg_out_phase, (i>1),false)) then
					begin
						pByte := addr(pByte^, 1);
						XferCode := ord(pByte^);
						i := i - 1;
					end
					else
					begin
						SynchState := NoSynch;
						ResultCode := ord(ScsiErr);
						utlSetInternalErr(PtrSessionBlock, ScsiManXferErr);
					end;
				end;

				if (ResultCode = ord(NoEvent)) then {all synched!}
				begin
					{all parms are o.k., send back }
					SynchState := Synched;
					TMODvalue.sx := true;
					TMODvalue.maxto := SynchParms.SynchReqAck MOD 8;
					TMODvalue.mintp := N;
					TMODvalue.pad := 0;
				end;
			end;
			{ else a Reject Msg has already been sent. }
		end;
	end;
end;

{*************************************************************************
 *  MsgGetMsg
 *************************************************************************
 *
 * On Input: Bus is in the MsgIn phase.
 *
 *
 * Functional Description:
 *
 *      Acquire the incomming message.  If the message has data that
 *      needs to be stored for later reference, store in either the
 *      sessionblock or in the select code block.
 *
 *      If a parity error is detected, send the MessageParityError
 *      message and await a retry.
 *
 * On Output:  Message has been acquired and the message code is set in
 *      the ResultCode.  ScsiErr if an error has occured.
 *
 *************************************************************************}
Procedure MsgGetMsg             (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);

type
	XlateMsgType = array [0..12] of S_SHORT;
const
	XlateMsgCode = XlateMsgType[    ORD(CmdComplete),ORD(ScsiErr),ORD(SaveDataPtr),
					ORD(RestorePtrs),ORD(Disconnect),ORD(ScsiErr),
					ORD(ScsiErr),ORD(ScsiErr),ORD(ScsiErr),ORD(ScsiErr),
					ORD(LCC),ORD(LCCwFlag),ORD(ScsiErr)];
var
	Buf:packed array [ 1 .. 5 ] of char;
	XferCode:s_byte;
	i : integer;
begin

	with PtrSessionBlock^.InternalBlock, pScBlock^.PtrScsiChip^ do
	begin
		ResultCode := ord(NoEvent);

		{
		 read in the message.
		  - special note: if this is a synchronous parms message comming in, then
		    before the last byte is brought in, we have to request message out phase
		    for a reply.
		}
		i := 0;
		repeat
			if hwiManXfer(PtrSessionBlock, XferCode, msg_in_phase,
				      (i<4), ((i = 4) and (ord(buf[3]) = 1))) then
			begin
				i := i + 1;
				Buf[i] := chr(XferCode);
			end
			else if (psns.t_phase = msg_in_phase) then
			begin
				ResultCode := ord(ScsiErr);
				utlSetInternalErr(PtrSessionBlock, ScsiManXferErr);
			end;
			{
			  else if the phase has changed, then this is not an error.
			}
		until ( (ResultCode <> ord(NoEvent)) or
			(psns.t_phase <> msg_in_phase) or
			(Buf[1] <> #1)
		      );

		XferCode := ord(Buf[1]);
		if (i = 1) then {standard 1 byte message}
		begin
			if (XferCode div 128) <> 0 then   { Identify }
				ResultCode := ORD(Identify)
			else if XferCode > hex('0c') then
				Resultcode := ord(ScsiErr)
			else
				ResultCode := XlateMsgCode[XferCode];

			{
			  Wait for a possible phase change - there may be more
			  message bytes.  This is determined by the state of the REQ
			  line.
			}
			if (psns.t_phase = msg_in_phase) and (not psns.req) then
			begin
				osdStartTimer(one_second);
				repeat
				until ( (psns.t_phase <> msg_in_phase) or
					( (psns.t_phase = msg_in_phase) and (psns.req) ) or
					(osdTimeExpired)
				      );

				if (psns.t_phase = msg_in_phase) and (not psns.req) then
				begin
					utlSetInternalErr(PtrSessionBlock, ScsiPhaseErr);
					ResultCode := ord(ScsiErr);
				end;
			end;
		end
		else if (XferCode = hex('01')) {extended message} and
			(i = 5) and
			(ord(Buf[3]) = hex('01')) and {synchronous message}
			(psns.t_phase = msg_out_phase) then {guarantee msgout phase}
		begin
			ResultCode := ord(Synch);
			TargetSynchPeriod := ord(Buf[4]);
			TargetSynchReqAck := ord(Buf[5]);
		end
		else {unknown message}
			ResultCode := ord(ScsiErr);


		if ResultCode = ORD(ScsiErr) then
			utlSetInternalErr(PtrSessionBlock, ScsiBadMsg);
	end;
end;

{*************************************************************************
 *  MsgRejectMsg
 *************************************************************************
 *
 * On Input: Bus can be in any phase.
 *
 *
 * Functional Description:
 *
 *      Acquire the MsgOut phase (hwiGetMsgOut)
 *      Format and send a MessageReject Message.
 *
 * On Output:  NoEvent if successful. ScsiErr otherwise.
 *
 *************************************************************************}
Procedure MsgRejectMsg          (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
  Xfer_Code : s_byte;
begin
    hwiGetMsgOut(PtrSessionBlock,ResultCode);
    if ResultCode = ORD(MsgOut) then begin
	xfer_code:=hex('07');
	if hwiManXfer(PtrSessionBlock,xfer_code,msg_out_phase,false,false) then
	  ResultCode := ORD(NoEvent)
	else
	begin
	  ResultCode := ORD(ScsiErr);
	  utlSetInternalErr(PtrSessionBlock, ScsiManXferErr);
	end;
    end;
end;


{*************************************************************************
 *  MsgSetXfer
 *************************************************************************
 *
 * On Input: Bus is in either CmdPhase or DataPhase.
 *
 *
 * Functional Description:
 *
 *      Set up the XferBlock which is located in the InternalBlock of the
 *      SessionBlock.
 *
 *      The XferPhase is the current bus phase.
 *      The XferRetryCount is set to 0.
 *      The XferBlock is set up according to the bus phase:
 *
 *              CmdPhase:       Use the CmdPtr and CmdLen.
 *                              DoDMA is always false.
 *                              Set up TMOD: if the device has synched, used TMODvalue.
 *
 *              DataIn or DataOut: Use BufIn or BufOut.
 *
 *      Set XferSavedDataPointer and XferSavedDateLength equal to BufPtr and BufLen.
 *
 * On Output: NoEvent.  XferBlock has been set up for a transfer.
 *            ScsiErr.  Illegal value in the XferBlock.
 *
 *************************************************************************}
Procedure MsgSetXfer            (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
	locTMOD:tmod_type;
begin
	ResultCode := ORD(NoEvent);
	With PtrSessionBlock^, InternalBlock.XferBlock, InternalBlock.pScBlock^, DeviceSynchParms[Device] do
	begin
		XferPhase := PtrScsiChip^.psns.t_phase;

		XferRetryCount := 0;

		case XferPhase of
			cmd_phase:
				begin
					XferBufBlock.BufPtr := CmdPtr;
					XferBufBlock.BufLen := CmdLen;
					XferBufBlock.DoDMA := false;

					{
					  Before entering data phase, the chip must see the
					  transfer method.
					}
					if (SynchState = Synched) then
						locTMOD.tmod := TMODvalue.tmod
					else
						locTMOD.tmod := hex('00');
					PtrScsiChip^.tmod.tmod := locTMOD.tmod;
				end;
			data_out_phase:
				XferBufBlock := BufOut;
			data_in_phase:
				XferBufBlock := BufIn;
			otherwise
				begin
					utlSetInternalErr(PtrSessionBlock, ScsiPhaseErr);
					ResultCode := ORD(ScsiErr);
				end;
		end;

		if (XferBufBlock.BufPtr = Nil) or
		   (XferBufBlock.BufLen <= 0) or
		   (XferBufBlock.BufLen > hex('00ffffff')) then
		begin
			utlSetInternalErr(PtrSessionBlock, ScsiXferParmErr);
			ResultCode := ORD(ScsiErr);
		end
		else if (odd(ord(XferBufBlock.BufPtr))) or (odd(XferBufBlock.BufLen)) then
			XferBufBlock.DoDMA := FALSE;

		if ResultCode = ORD(NoEvent) then {no errors have occured so far.}
		begin
			XferSavedDataPointer :=
						XferBufBlock.BufPtr;
			XferSavedDataLength  :=
						XferBufBlock.BufLen;
		end;
	end;
end;



{*************************************************************************
 *  MsgDoXfer
 *************************************************************************
 *
 * On Input: Bus is in correct phase and the XferBlock has been set up.
 *
 *
 * Functional Description:
 *
 *      Sets up the hardware for a transfer and controls the transfer.
 *      If DoDMA, sets up DMA and waits for an interrupt.
 *
 *      If an error occurs, this routine handles the error and waits until
 *      the bus is in message out phase (calls hwiGetMsgOut).
 *
 *      If the xfer is stopped by the target, this is o.k., however this
 *      routine must see that any untransmitted data that is in the chip/DMA
 *      buffers are cleared out and that the buffer pointer and length is
 *      set to the next byte to be transferred.
 *
 * On Output:   ScsiErr if an unrecoverable hardware error occurs.
 *              MsgOut if error occured and bus successfuly moved to MsgOut phase.
 *              NoEvent otherwise.
 *
 *************************************************************************}
Procedure MsgDoXfer             (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	With PtrSessionBlock^, InternalBlock, XferBlock, pScBlock^, PtrScsiChip^ do
	begin
		ResultCode := ord(NoEvent);

		{
		  if not waiting for an ISR, then starting or renewing the
		  transfer defined in the XferBlock.
		}
		if NOT ISRWaiting then
		begin
			{
			  Is DMA Requested on this buffer block?
			  if so, attempt to get a DMA channel.
			  If one is unavailable, convert this xfer to
			  a programmatic xfer.
			}
			if XferBlock.XferBufBlock.DoDMA then
			begin
				DMAChannel := osdGetDMAChannel(SelectCode);
				if DMAChannel = -1 then
					XferBufBlock.DoDMA:=false;
			end;

			{
			  If a DMA channel has been acquired, then start the DMA.
			  Otherwise, this is a programmatic transfer.
			}
			if XferBlock.XferBufBlock.DoDMA then
			begin
				DMAInProgress := TRUE;
				if (not hwiStartDMAXfer(PtrSessionBlock)) then
					ResultCode := ord(ScsiErr)
				else
					ResultCode := ord(WaitForISR);
			end
			else {programmatic xfer}
			begin
				if (not hwiProgXfer(PtrSessionBlock)) then
					ResultCode := Ord(ScsiErr);
			end;
		end
		else {chip interrupt after DMA}
		begin
			if (not hwiEndDMAXfer(PtrSessionBlock, ResultCode)) then
			begin
				{
				  DMAXfer has completed, but needs to be restarted to
				  complete the overall transfer
				}
				DMAInProgress := TRUE;
				if (not hwiStartDMAXfer(PtrSessionBlock)) then
					ResultCode := ord(ScsiErr)
				else
					ResultCode := ord(WaitForISR);
			end
			else
			begin
				{
				  DMAXfer has completed, ResultCode contains the state machine
				  result.
				}
				osdReleaseDMAChannel(SelectCode);
				DMAInProgress := FALSE;
			end;
		end;


		if (ResultCode = ord(NoEvent)) and (psns.t_phase = XferPhase) then
		begin
			{
			  wait for the peripheral to change to the next phase

			  if the driver has requested the correct amount of data,
			  but the target is expecting more data (happens if user
			  gives incorrect parms) then could wait forever and
			  the phase will not change.  But how long to wait?  What
			  if the command were a format command which can take
			  30 minutes or longer!  The phase still wouldn't change
			  until the disc has formatted.  There is nothing that
			  can be done in this case - PWS will hang.
			}
			repeat until (psns.t_phase <> XferPhase) or (ints.ints <> 0);
			if (psns.t_phase = XferPhase) then
			begin
				utlSetInternalErr(PtrSessionBlock, ScsiXferErr);
				ResultCode := Ord(ScsiErr);
			end;
		end;
	end; {with}
end;

{*************************************************************************
 *  MsgSawErrMsg
 *************************************************************************
 *
 * On Input:    Bus is in MsgOut phase and a transmission error has been
 *              detected during a Command or Data phase.
 *
 * Functional Description:
 *
 *      Send a InitiatorDetectedError message to the target.
 *      Do not raise the ATN line during the transfer.
 *
 *
 * On Output:   NoEvent if message successfully sent.
 *              ScsiErr otherwise.
 *
 *************************************************************************}
Procedure MsgSawErrMsg          (PtrSessionBlock:PtrSessionBlockType;
				 Var ResultCode:S_SHORT);
var
  xfer_code : s_byte;

begin
	with PtrSessionBlock^.InternalBlock.XferBlock do
	begin
	    XferRetryCount := XferRetryCount + 1;
	    if XferRetryCount > XferRetryMax then
	    begin
		ResultCode := ORD(ScsiErr);
		utlSetInternalErr(PtrSessionBlock, XferRetryErr);
	    end
	    else
	    begin
		xfer_code:=hex('05');
		if hwiManXfer(PtrSessionBlock,xfer_code,msg_out_phase,false,false) then
		  ResultCode := ORD(NoEvent)
		else
		begin
		  ResultCode := ORD(ScsiErr);
		  utlSetInternalErr(PtrSessionBlock, ScsiXferErr);
		end;
	    end;
	 end;
 end;

{*************************************************************************
 *  MsgPrepareDisc
 *************************************************************************
 *
 * On Input:    A Save Data Pointers message has just been received.
 *              This means that the target is either getting ready to
 *              disconnect or wants to ensure that the transmission done
 *              so far is not lost if an error occurs latter on.
 *
 * Functional Description:
 *
 *      Copy the current buffer pointer and length to the save pointer
 *      and length.
 *
 * On Output:   NoEvent
 *
 *
 *************************************************************************}
Procedure MsgPrepareDisc        (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	With PtrSessionBlock^.InternalBlock.XferBlock do
	begin
		XferSavedDataPointer := XferBufBlock.BufPtr;
		XferSavedDataLength := XferBufBlock.BufLen;
	end;
	ResultCode := Ord(NoEvent);
end;

{*************************************************************************
 *  MsgSetDisc
 *************************************************************************
 *
 * On Input:    A disconnect message has been received.
 *
 *
 * Functional Description:
 *
 *      wait for disconnect to happen.
 *      turn off disconnect interrupt
 *      Set session state to suspended.
 *
 * On Output:   BusFree if disconnect occurs, ScsiErr otherwise.
 *
 *
 *************************************************************************}
Procedure MsgSetDisc            (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
label 1;
begin
	with PtrSessionBlock^, InternalBlock.pScBlock^.PtrScsiChip^ do
	begin
		if not ints.disconnected then
		begin
			osdStartTimer(one_second);
			repeat until (ints.disconnected) or osdTimeExpired;
			if not ints.disconnected then
			begin
				utlSetInternalErr(PtrSessionBlock, ScsiTimeOutErr);
				ResultCode := ORD(ScsiErr);
				goto 1;
			end;
		end;
		ints.ints := disconnected; { turn off disconnected interrupt }
		SessionState := SessionSuspended;
		ResultCode := ORD(BusFree);
	end;
	1:
end;

{*************************************************************************
 *  MsgPrepareRetry
 *************************************************************************
 *
 * On Input:    A RestorePtrs messgae has just been recieved.
 *              This means that the target is recovering from an error
 *              or is returnning from a disconnect.  If recovering from
 *              an error, then when the next transmission occurs, the
 *              transmission will begin from the last time the pointers
 *              were saved (beginning of transfer or save data pointers msg).
 *
 * Functional Description:
 *
 *      Copy the save pointer and length to the buffer pointer and length.
 *
 * On Output:   NoEvent
 *
 *
 *************************************************************************}
Procedure MsgPrepareRetry       (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	With PtrSessionBlock^, InternalBlock, XferBlock, pScBlock^,
	     PtrScsiChip^, DeviceSynchParms[Device] do
	begin
		XferBufBlock.BufPtr := XferSavedDataPointer;
		XferBufBlock.BufLen := XferSavedDataLength;
	end;
	ResultCode := Ord(NoEvent);
end;

{*************************************************************************
 *  MsgIsOriginalPhase
 *************************************************************************
 *
 * On Input: XferBlock in SessionBlock contains original h/w phase.
 *
 *
 * Functional Description:
 *
 *      obtain the current bus phase (EventType) and compare with what is in the
 *      Xfer block.
 *
 *
 *
 *
 * On Output:  IsOriginalPhase if match is good, otherwise EventType bus phase.
 *
 *
 *************************************************************************}
Procedure MsgIsOriginalPhase    (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	hwiGetPhase(PtrSessionBlock, ResultCode);
	with PtrSessionBlock^.InternalBlock.XferBlock,
	     PtrSessionBlock^.InternalBlock.pScBlock^.PtrScsiChip^ do
	begin
		if (ResultCode = ORD(CmdPhase)) and (XferPhase = cmd_phase) then
			ResultCode := ORD(OriginalPhase)
		else if (ResultCode = ORD(DataPhase)) then
		begin
			if (psns.io) then
			begin
				if (XferPhase = data_in_phase) then
					ResultCode := ORD(OriginalPhase);
			end
			else
			begin
				if (XferPhase = data_out_phase) then
					ResultCode := ORD(OriginalPhase);
			end;
		end;
	end;
end;

{*************************************************************************
 *  MsgWaitReselect
 *************************************************************************
 *
 * On Input:    The bus is in a bus free state.  The target must reselect
 *              this session.  It is possible that the reselect has
 *              already occured.
 *
 * Functional Description:
 *
 *      Set up for an interrupt to occur when target reselects this session.
 *      When reselected, set the session state to running, and resets the current transfer.
 *
 * On Output:   ScsiErr if timeout, otherwise NoEvent.
 *
 *************************************************************************}
Procedure MsgWaitReselect       (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
	xfer_code : s_byte;
	locTMOD:tmod_type;
begin
	with PtrSessionBlock^, InternalBlock, pScBlock^,
	     PtrScsiCard^, PtrScsiChip^, DeviceSynchParms[Device] do
	if (not ISRWaiting) and (not ints.reselected) then
	begin
		ResultCode := ORD(WaitForISR);
		ISRWaiting := True;
		ISRMask := Reselected;
		sc_reg.ie := true;
	end
	else {ISR has occured or Reselected has already happened}
	begin
		sc_reg.ie := false;
		ints.ints := ints.ints;
		ISRWaiting := false;
		SessionState := SessionRunning;
		{
		  tmod value must be set up before chip sees data in/out transfer
		  phase.
		}
		if (SynchState = Synched) then
			locTMOD.tmod := TMODvalue.tmod
		else
			locTMOD.tmod := hex('00');
		tmod.tmod := locTMOD.tmod;

		ResultCode := ord(MsgIn);
		if ISRError then
			ResultCode := ORD(ScsiErr)
		else if (psns.t_phase <> msg_in_phase) then
		begin
			osdStartTimer(one_second);
			repeat until ( (psns.t_phase = msg_in_phase) or (osdTimeExpired) );

			if (psns.t_phase <> msg_in_phase) then
			begin
				utlSetInternalErr(PtrSessionBlock, ScsiPhaseErr);
				ResultCode := ord(ScsiErr);
			end;
		end;
	 end;
end;

{*************************************************************************
 *  MsgIsPathActive
 *************************************************************************
 *
 * On Input:    Nothing expected
 *
 *
 * Functional Description:
 *
 *      Checks if the session is currently talking to a device.
 *      If the session is running and the chip is connected, then the
 *      path is active.
 *
 *      If the path is not active, return if the bus is busy or free.
 *
 * On Output:   NoEvent if path is active, BusFree or BusBusy otherwise.
 *
 *************************************************************************}
Procedure MsgIsPathActive       (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	with PtrSessionBlock^, InternalBlock.pScBlock^.PtrScsiChip^ do
	begin
		if (SessionState = SessionRunning) and (ssts.init) then
			ResultCode := ORD(NoEvent)
		else if psns.bsy then
			ResultCode := ORD(BusBusy)
		else
			ResultCode := ORD(BusFree);
	end;
end;

{*************************************************************************
 *  MsgAbortMsg
 *************************************************************************
 *
 * On Input: bus is in message out phase.
 *
 *
 * Functional Description:
 *
 *      Transfer an AbortMessage to the target.
 *
 *
 *
 *
 * On Output:   ScsiErr or NoEvent.
 *
 *
 *************************************************************************}
Procedure MsgAbortMsg           (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
   xfer_code : s_byte;

begin
    xfer_code:=hex('06');
    if hwiManXfer(PtrSessionBlock,xfer_code,msg_out_phase,false,false) then
      ResultCode := ORD(NoEvent)
    else
    begin
      ResultCode := ORD(ScsiErr);
      utlSetInternalErr(PtrSessionBlock, ScsiXferErr);
    end;
end;

end;

@


56.1
log
@Automatic bump of revision number for PWS version 3.25
@
text
@d200 1
a200 1
					DeviceSynchParms[Device].SynchState := NeedSynch;
d511 1
a511 1
		SynchState := NeedSynch;
@


55.1
log
@Automatic bump of revision number for PWS version 3.25A
@
text
@@


54.1
log
@Automatic bump of revision number for PWS version 3.24
@
text
@@


53.1
log
@Automatic bump of revision number for PWS version 3.24B
@
text
@@


52.1
log
@Automatic bump of revision number for PWS version 3.24A
@
text
@@


51.1
log
@Automatic bump of revision number for PWS version 3.24d
@
text
@@


50.1
log
@Automatic bump of revision number for PWS version 3.23c
@
text
@@


49.1
log
@Automatic bump of revision number for PWS version 3.24b
@
text
@@


48.1
log
@Automatic bump of revision number for PWS version 3.24a
@
text
@@


47.1
log
@Automatic bump of revision number for PWS version 3.23
@
text
@@


46.1
log
@Automatic bump of revision number for PWS version 3.23
@
text
@@


45.2
log
@
MsgSelect

	Time for SelectRetry is increased from 1 ms to 250 ms.  This
	matches spec.  Also, the if statement for the SelectRetryCount
	was made safe.
@
text
@@


45.1
log
@Automatic bump of revision number for PWS version 3.23C
@
text
@d178 2
a179 2
			data_regs.tch := #0;
			data_regs.tcm := #32;
d196 1
a196 1
				if InternalBlock.SelectRetryCount = SelectRetryMax then
@


44.1
log
@Automatic bump of revision number for PWS version 3.23B
@
text
@@


43.1
log
@Automatic bump of revision number for PWS version 3.23aA
@
text
@@


42.5
log
@Fix for optical disc problem with disconnect.  On reselect, the optical disc
was not setting the bus to a proper state very fast.  In fact, after the reselect,
the driver would check the bus phase and determine that it was not correct and
we would get a ScsiPhaseErr.  The fix? Update MsgWaitReselect to wait for a
message in phase before giving up control.  It used to be that any bus phase
after reselect was accepted, but this is no longer the case.  After reviewing the
ANSI spec and the fujitsu spec, it has become apparent that the device MUST
re-establish the path through an identify message.
@
text
@@


42.4
log
@MsgWaitTimeOrFree no longer waits after a disconnect
and before a reselect.  Thus, much of the code complication
could be cleaned up.
@
text
@d1190 1
a1191 1
		ints.ints := ints.ints;
d1202 2
a1203 1
		ResultCode := ord(NoEvent);
d1206 11
@


42.3
log
@msgSelect: make sure device being selected is not ourself!

msgSetXfer: Added some comments and additional error checking of the
	    transfer parms.

msgSetDisc:  Rewrote entirely!  do not set reselect enable interrupt.
             wait for disconnect interrupt to occur, then reset it.
             don't make an ISR happen because it should be instantaneous.

msgWaitReselect: don't reset reselect enable interrupt.
		 if reselect interrupt has allready happened, handle it
		 immediately - don't force an ISR.
		 set tmod register up here.  must occur before chip sees
		 data in/out phase.
@
text
@d116 1
a116 1
		if (ssts.init) and (not ints.reselected) then
d120 1
a120 1
			until (not ssts.init) or (ints.reselected) or (osdTimeExpired);
d122 1
a122 1
			if (ssts.init) and (not ints.reselected) then
a127 1
		ints.disconnected := false;
@


42.2
log
@Turns that the ResetXfer routine was never used, so for effeciency
it has been removed.
@
text
@a106 1
 *
d158 2
d164 21
a184 12
		ResultCode := ORD(WaitForISR);
		InternalBlock.ISRWaiting := True;
		InternalBlock.ISRMask := cmd_complete + timeout;
		ints.ints := ints.ints;
		pctl.pctl := 0;
		data_regs.temp := chr(osdBitOr(ord(bdid), osdBitLsL(1,Device)));
		scmd.cmd := set_atn_cmd;
		data_regs.tch := #0;
		data_regs.tcm := #32;
		data_regs.tcl := #4;
		scmd.cmd := select_cmd;
		sc_reg.ie := true;
d790 1
d810 5
d832 8
a839 1
		if (odd(ord(XferBufBlock.BufPtr))) or (odd(XferBufBlock.BufLen)) then
d952 9
d965 1
a966 1
				utlSetInternalErr(PtrSessionBlock, ScsiXferErr);
d1054 2
d1058 1
a1058 1
 * On Output:   NoEvent
d1063 1
d1065 1
a1065 1
	with PtrSessionBlock^.InternalBlock.pScBlock^.PtrScsiChip^ do
d1067 14
a1080 5
		if ints.reselected then
			ints.ints := ints.ints - reselected
		else
			ints.ints := ints.ints;
		sctl.re := true;
d1082 1
a1082 2
	PtrSessionBlock^.SessionState := SessionSuspended;
	ResultCode := ORD(NoEvent);
a1104 2
var
	locTMOD:tmod_type;
a1110 5
		if (SynchState = Synched) then
			locTMOD.tmod := TMODvalue.tmod
		else
			locTMOD.tmod := hex('00');
		tmod.tmod := locTMOD.tmod;
d1163 2
a1164 1
 *              this session.
d1177 1
d1181 1
a1181 1
	if not ISRWaiting then
a1185 1
		{reselect enable set up after disconnect message received}
d1188 1
a1188 1
	else {ISR has occured}
a1190 1
		sctl.re := false;
d1194 9
@


42.1
log
@Automatic bump of revision number for PWS version 3.23e
@
text
@a32 1
	Procedure MsgResetXfer          (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
a828 28
{*************************************************************************
 *  MsgResetXfer
 *************************************************************************
 *
 * On Input: Transfer is to be continued. Original Xfer Phase is on
 *           scsi bus.
 *
 *
 * Functional Description:
 *
 *      Do any required updates to the XferBlock so that the Transfer
 *      can successfully be restarted.
 *
 *      Not sure this procedure is required.
 *      The only item that should be done here is the Saved pointer and length
 *      should be moved to the XferBufBlock.  However, the Restore Pointers
 *      Message/or a reconnect are the only times this should happen (by
 *      definition, the target owns the saved information and controls when
 *      it is restored).
 *
 * On Output:
 *
 *
 *************************************************************************}
Procedure MsgResetXfer          (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	ResultCode := ORD(NoEvent);
end;
@


41.5
log
@
msgGetMsg updated to handle back to back single byte messages.

msgSetDisc updated to set reselect enable, and clear ints.

WaitReselect cleaned up so that decision making is correctly handled at
the state table/machine level.
@
text
@@


41.4
log
@Secondary Logical Units are not supported by the Scsi Bus Driver.
Took out some comments that indicated they were.
@
text
@d654 1
a654 2
		while (psns.t_phase = msg_in_phase) and (ResultCode = ord(NoEvent)) do
		begin
d656 1
a656 1
				      (i<4), ((i = 4) and (ord(buf[1]) = 1) and (ord(buf[3]) = 1))) then
d669 4
a672 1
		end;
d683 21
d1059 8
d1091 2
d1094 2
a1095 1
	With PtrSessionBlock^.InternalBlock.XferBlock do
d1099 5
a1168 1
	locTMOD:tmod_type;
d1176 2
a1177 3
		ISRMask := Reselected ;
		ints.ints := ints.ints;
		sctl.re := true;
d1187 1
a1189 34
		else
		begin
			hwiGetPhase(PtrSessionBlock,ResultCode);
			if ResultCode = ORD(Msgin) then
			begin
				MsgGetMsg(PtrSessionBlock,ResultCode);
				If (ResultCode = ORD(Identify)) or (ResultCode = ORD(RestorePtrs)) then
				begin
					MsgPrepareRetry(PtrSessionBlock,ResultCode);
					if (SynchState = Synched) then
						locTMOD.tmod := TMODvalue.tmod
					else
						locTMOD.tmod := hex('00');
					tmod.tmod := locTMOD.tmod;
				end
				{
				  * else if ResultCode = ORD(CmdComplete) then
				  * begin
				  *     }{ do nothing, 7958S kludge }{
				  *     ResultCode := ORD(NoEvent);
				  * end
				}
				else
				begin
					ResultCode := ORD(ScsiErr);
					utlSetInternalErr(PtrSessionBlock, ScsiBadMsg);
				end;
			end
			else
			begin
				 ResultCode := ORD(ScsiErr);
				 utlSetInternalErr(PtrSessionBlock, ScsiXferErr);
			end;
		end;
@


41.3
log
@Rewrote MsgWaitTimeOrFree to not be fooled by bus going free, then 
host being reselected.  This routine was missing the bus free
if the host reselected before it was called.
@
text
@d248 3
a250 3
 *      Format and transmit an identify message to the device.  If the LUN
 *      has the upper bit set, then an extended identify is required.
 *      Range checking on the LUN value and the SUN value is performed here.
a278 1
	{tbd handle extended identify}
@


41.2
log
@Before doing DMA, should also verify that the length is not an odd value.
@
text
@d117 6
a122 6
		pctl.pctl := 0;
		ints.ints := ints.ints;
		pctl.bfree_ie := true;
		osdStartTimer(one_second);
		repeat
		until (ints.ints <> 0) or osdTimeExpired;
d124 5
a128 8
		if ints.disconnected then
		begin
			ResultCode := ORD(BusFree);
		end
		else
		begin
			utlSetInternalErr(PtrSessionBlock, ScsiTimeOutErr);
			ResultCode := ORD(ScsiErr);
d130 1
a130 2
		pctl.bfree_ie := false;
		ints.ints := ints.ints;
@


41.1
log
@Automatic bump of revision number for PWS version 3.23d
@
text
@d799 1
a799 1
		if odd(ord(XferBufBlock.BufPtr)) then
@


40.7
log
@Made sure DMA address for 16 bit DMA is on a word boundary.

Fixed a timeout bug discovered wtih tapes.
@
text
@@


40.6
log
@Took the terrible gotos out of MsgDoXfer, and replaced with better
structured code.
@
text
@d798 4
d941 1
a941 2
			osdStartTimer(10*one_second);
			repeat until (psns.t_phase <> XferPhase) or osdTimeExpired;
d945 1
a945 1
				utlSetInternalErr(PtrSessionBlock, ScsiPhaseErr);
@


40.5
log
@Modifications for DMA support.
@
text
@a863 2
label
	1, 2;
a893 1
				2:
a898 1
				goto 1;
a903 3

				if ResultCode = ORD(ScsiErr) then
					goto 1;
d914 5
a918 1
				goto 2;
a927 2
				if (ResultCode = ORD(ScsiErr)) then
					goto 1;
d932 1
a932 4
		{
		  wait for the peripheral to change to the next phase
		}
		if (psns.t_phase = XferPhase) then
d934 3
a942 1
				goto 1;
a945 1
	1:
@


40.4
log
@msgGetMsgOut is moved to hwiGetMsgOut.
MsgDoXfer is modified to call hwiProgXfer.
@
text
@d339 1
a339 1
	DMA32Bit:integer;
d349 2
a350 2
		DMA32bit := ord( ((osdDMA32bit) and (sc_reg.dma_32)) );
		SynchParms.SynchPeriod := PeriodXlate[config_reg.sync_rate][DMA32bit];
d446 2
a447 1
								TMODvalue.maxto := SynchMsgBuf.SynchReqAck;
d499 1
a499 1
	DMA32Bit:integer;
d511 2
a512 2
		DMA32bit := ord( ((osdDMA32bit) and (sc_reg.dma_32)) );
		SynchParms.SynchPeriod := PeriodXlate[config_reg.sync_rate][DMA32bit];
d603 1
a603 1
					TMODvalue.maxto := SynchParms.SynchReqAck;
d865 1
a865 1
	1;
d867 1
a867 1
	With PtrSessionBlock^.InternalBlock, XferBlock, pScBlock^.PtrScsiChip^ do
d869 7
a875 2
		XferBufBlock.DoDMA:=false;  { tbd - change later !! }
		if XferBufBlock.DoDMA then
d877 35
a911 1
		     { see unix page 86 for dma transfer }
d913 1
a913 1
		else {programmatic xfer}
d915 1
a915 1
			if (not hwiProgXfer(PtrSessionBlock)) then
d917 16
a932 2
				ResultCode := Ord(ScsiErr);
				goto 1;
@


40.3
log
@Updated to reflect SCSI_DEFS modifications for programmer's interface.
@
text
@a40 1
	Procedure MsgGetMsgOut          (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
d713 1
a713 1
 *      Acquire the MsgOut phase (MsgGetMsgOut)
d723 1
a723 1
    MsgGetMsgOut(PtrSessionBlock,ResultCode);
d850 1
a850 1
 *      the bus is in message out phase (calls msgGetMsgOut).
a864 3
Var
	SaveInts:ints_type;
	xferbyte:s_byte;
d866 1
a866 1
	With PtrSessionBlock^, InternalBlock.XferBlock, InternalBlock.pScBlock^.PtrScsiChip^ do
d875 1
a875 26
			{
				set up transfer count
			}
			data_regs.tch := chr(XferBufBlock.BufLen Div HEX('100') Div HEX('100'));
			data_regs.tcm := chr(XferBufBlock.BufLen Div HEX('100'));
			data_regs.tcl := chr(XferBufBlock.BufLen);


			{
				tell the chip which phase we expect to xfer in.
			}
			pctl.t_phase := XferPhase;



			{
			 tell chip to commence a programmatic transfer
			}
			ints.ints := ints.ints;
			scmd.scmd := transfer_prgx;


			{
			wait for chip to respond
			}
			if ssts.action <> spc_init_xfer then
a876 71
				osdStartTimer(one_second);
				repeat until (ssts.action = spc_init_xfer) or osdTimeExpired;
				if ssts.action <> spc_init_xfer then
				begin
					ResultCode := Ord(ScsiErr);
					utlSetInternalErr(PtrSessionBlock, ScsiXferErr);
					goto 1;
				end;
			end;



			{
				execute the programmatic xfer
				if read, wait for data in fifo, then read data register
				if write, wait for fifo not full, then write data register
			}
			if (XferPhase = data_in_phase) {read} then
			while (XferBufBlock.BufLen > 0) and (ints.ints = 0) do
			begin
				if (not ssts.dempty) then
				begin
					PtrChar(XferBufBlock.BufPtr)^ := data_regs.dreg;
					XferBufBlock.BufPtr := addr(XferBufBlock.BufPtr^,1);
					XferBufBlock.BufLen := XferBufBlock.BufLen - 1;
				end
				else
				begin
					repeat until (not ssts.dempty) or (ints.ints <> 0);
				end;
			end
			else {write}
			while (XferBufBlock.BufLen > 0) and (ints.ints = 0) do
			begin
				if (not ssts.dfull) then
				begin
					data_regs.dreg := PtrChar(XferBufBlock.BufPtr)^;
					XferBufBlock.BufPtr := addr(XferBufBlock.BufPtr^,1);
					XferBufBlock.BufLen := XferBufBlock.BufLen - 1;
				end
				else
				begin
					repeat until (not ssts.dfull) or (ints.ints <> 0);
				end;
			end;

			{
			  don't need to wait for chip to finish up work because waiting for
			  an interrupt below.
			}

		end; {programatic xfer}


		{
			Interrupt handling for both DMA and Programmatic xfers
			is the same.

			Interrupts handled here are: cmd_complete, svc_required,
			and spc_hard_err.
		}

		{
		  an interrupt of some kind must come through, wait for it.
		}
		if (ints.ints = 0) then
		begin
			osdStartTimer(10*one_second);
			repeat until (ints.ints <> 0) or osdTimeExpired;
			if (ints.ints = 0) then
			begin
a877 1
				utlSetInternalErr(PtrSessionBlock, ScsiXferErr);
a880 7
		Saveints.ints := ints.ints;
		ints.ints := ints.ints;
		if serr.serr <> 0 then
			Saveints.spc_hard_err := true;
		if (Saveints.spc_hard_err) then
		begin
			Saveints.spc_hard_err := false;
a881 7
			{
				call msgGetMsgOut which will force the bus to message out
				phase (if target still expects to xfer data, this routine will
				pump no-op data, or read and throw away data, until message
				out phase has been granted.
			}
			msgGetMsgOut(PtrSessionBlock, ResultCode);
a882 39
			{
			reset the transfer control hardware on the chip.
			}
			sctl.cr := TRUE;  sctl.cr := FALSE;
		end;

		if (Saveints.svc_required) then
		begin
			Saveints.svc_required := false;

			{
				get the residual count from tch, tcm, and tcl; this
				is the new buffer length.  Adjust the buffer pointer
				to point to the next byte to be transfered.
			}
			XferBufBlock.BufLen := (ord(data_regs.tch) * HEX('100') * HEX('100')) +
					       (ord(data_regs.tcm) * HEX('100'))   +
					       ord(data_regs.tcl);

			XferBufBlock.BufPtr := addr(XferSavedDataPointer^,
						XferSavedDataLength - XferBufBlock.BufLen);

			{
				reset the transfer control hardware on the chip.
			}
			sctl.cr := TRUE;  sctl.cr := FALSE;
		end;

		if (Saveints.cmd_complete) then
			Saveints.cmd_complete := false;

		if (Saveints.ints <> 0) {an unexpected interrupt has occured} then
		begin
			ResultCode := Ord(ScsiErr);
			utlSetInternalErr(PtrSessionBlock, ScsiInteruptErr);
			goto 1;
		end;


a1172 73
end;

{*************************************************************************
 *  MsgGetMsgOut
 *************************************************************************
 *
 * On Input:    Path is active, but bus is not guaranteed to be in any
 *              particular phase.  A transfer may currently be active.
 *
 * Functional Description:
 *
 *      Get the bus to a MsgOut phase.  If a transfer is active, finish
 *      it up first.
 *
 * On Output:   MsgOut or ScsiErr.
 *
 *
 *************************************************************************}
Procedure MsgGetMsgOut          (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
label 1;
Var
      XferCount:integer;
      b        :char;
begin
    with PtrSessionBlock^.InternalBlock.pScBlock^.PtrScsiChip^ do
    begin
	XferCount := osdBitLsL(ord(data_regs.tch), 16) + osdBitLsL(ord(data_regs.tcm), 8) +
		     ord(data_regs.tcl) + 1024;
	scmd.cmd := set_atn_cmd;  {Tell peripheral we want message out!}

	while (psns.t_phase <> msg_out_phase) and (XferCount > 0) do
	begin
	    {
		wait for peripheral to request a transfer
	    }
	    osdStartTimer(one_second);
	    repeat
	    until psns.req or osdTimeExpired;
	    if (not psns.req) or (psns.t_phase = msg_out_phase) then goto 1;

	    {
		Peripheral has requested a transfer and phase is NOT msg out
		NOTE: reading the data as if in asynchrnous mode always
		tbd - use programmatic mode this will work with synch protocol to!
	    }
	    pctl.t_phase := psns.t_phase;
	    if (psns.io) {input mode} then
	    begin
		scmd.cmd := set_ack_req_cmd;
		repeat
		until (not psns.req) or (psns.t_phase = msg_out_phase);
		b := data_regs.temp;
	    end
	    else {output mode}
	    begin
		data_regs.temp := #0;
		scmd.cmd := set_ack_req_cmd;
		repeat
		until (not psns.req) or (psns.t_phase = msg_out_phase);
	    end;
	    scmd.cmd := reset_ack_req_cmd;
	    XferCount := XferCount - 1;
	end;

	1:
	if psns.t_phase <> msg_out_phase then
	begin
	     ResultCode := ORD(ScsiErr);
	     utlSetInternalErr(PtrSessionBlock, ScsiPhaseErr);
	end
	else
	      ResultCode := ORD(MsgOut);
    end;
@


40.2
log
@Massive changes made to support synchrnous protocol.
MsgInitSynchParms and MsgTargetSynchParms were written.
MsgGetMsg was rewritten.
MsgGetMsgOut received modifications.
All references to hwiManXfer were modified.
@
text
@d1145 1
a1145 1
	PtrSessionBlock^.InternalBlock.SessionState := SessionSuspended;
d1318 1
a1318 1
	with PtrSessionBlock^.InternalBlock do
d1320 1
a1320 2
		if (SessionState = SessionRunning) and
		   (pScBlock^.PtrScsiChip^.ssts.init) then
d1322 1
a1322 1
		else if pScBlock^.PtrScsiChip^.psns.bsy then
@


40.1
log
@Automatic bump of revision number for PWS version 3.23c
@
text
@d46 8
d55 7
d63 33
d166 1
a166 2
	with PtrSessionBlock^, InternalBlock.pScBlock^.PtrScsiCard^,
	     InternalBlock.pScBlock^.PtrScsiChip^ do
d197 1
d264 1
a264 1
 * On Output: NoEvent if successful, otherwise ScsiErr.
d283 1
a283 1
	With IdentifyData, PtrSessionBlock^, InternalBlock.pScBlock^.PtrScsiChip^ do
d293 1
a293 3
		{tbd - Check if need to Synch Parms here.}
		{MaintainATN := not InternalBlock.pScBlock^.DeviceSynchParms[Device].DeviceSynched;}
		MaintainATN := false;
d295 1
a295 1
		if hwiManXfer(PtrSessionBlock, XferByte, msg_out_phase, MaintainATN) then
d337 7
d345 127
a471 1
	ResultCode := ORD(ScsiErr);
d481 1
d497 7
d505 106
a610 1
	ResultCode := ORD(ScsiErr);
d636 1
a636 1
   xlate_msg_type = array [0..12] of S_SHORT;
d638 4
a641 4
   xlate_msg_code = xlate_msg_type[ORD(CmdComplete),ORD(ScsiErr),ORD(SaveDataPtr),
				   ORD(RestorePtrs),ORD(Disconnect),ORD(ScsiErr),
				   ORD(ScsiErr),ORD(ScsiErr),ORD(ScsiErr),ORD(ScsiErr),
				   ORD(LCC),ORD(LCCwFlag),ORD(ScsiErr)];
d643 3
a645 2
  xfer_code : s_byte;

a646 8
    if hwiManXfer(PtrSessionBlock,xfer_code,msg_in_phase,false) then
    begin
      if (xfer_code div 128) <> 0 then   { Indentify }
	ResultCode := ORD(Identify)
      else if xfer_code > hex('0c') then
	Resultcode := ord(ScsiErr)
      else
	ResultCode := Xlate_Msg_Code[xfer_code];
d648 55
a702 8
      if ResultCode = ORD(ScsiErr) then
	utlSetInternalErr(PtrSessionBlock, ScsiBadMsg);
    end
    else
    begin
      ResultCode := ORD(ScsiErr);
      utlSetInternalErr(PtrSessionBlock, ScsiXferErr);
    end;
d725 1
a725 1
    if ResultCode = ORD(NoEvent) then begin
d727 1
a727 1
	if hwiManXfer(PtrSessionBlock,xfer_code,msg_out_phase,false) then
d756 1
a756 1
 *                              Protocol is always asynchronous.
a758 3
 *                              If Protocol is synchronous but synch parms
 *                              have not been set up (In the SelectCode
 *                              block), convert Protocol to Asynchronous.
d766 2
d770 1
a770 1
	With PtrSessionBlock^, InternalBlock.XferBlock, InternalBlock.pScBlock^ do
a780 1
					XferBufBlock.Protocol := fastest;
d782 5
a803 4

			if (XferbufBlock.Protocol = Synchronous) and
			   (not DeviceSynchParms[Device].DeviceSynched) then
				XferBufBlock.Protocol := Asynchronous;
a887 15
				tell the chip how to talk to the peripheral
			}
			tmod.tmod:=0;
			{
			 *      tbd - add synchronous communications.
			 *      if (InternalBlock.pScBlock^.DeviceSynchParms[Device].DeviceSynched)
			 *         and ((protocol = Synchronous) or (protocol = fastest)) then
			 *              tmod.sx := TRUE
			 *      else
			 *              tmod.sx := FALSE;
			}



			{
d1089 1
a1089 1
		if hwiManXfer(PtrSessionBlock,xfer_code,msg_out_phase,false) then
d1231 1
a1231 1
 *      When reselected, set the session state to running.
a1237 2
	pChip:PtrScsiChipType;
	pCard:PtrScsiCardType;
d1239 1
d1241 2
a1242 3
	pCard := hwiGetCardPtr(PtrSessionBlock);
	pChip := hwiGetChipPtr(PtrSessionBlock);
	with PtrSessionBlock^, InternalBlock, pCard^, pChip^ do
d1270 5
d1276 7
a1282 5
				else if ResultCode = ORD(CmdComplete) then
				begin
					{ do nothing, 7958S kludge }
					ResultCode := ORD(NoEvent);
				end
d1370 2
d1377 2
a1378 9
		if (tmod.sx = false) {not doing synchronous xfer} then
		begin
		    repeat
		    until (not psns.req) or (psns.t_phase = msg_out_phase);
		end
		else
		begin
		    { tbd - handle synchronous xfer }
		end;
d1385 2
a1386 9
		if (tmod.sx = false) {not doing synchronous xfer} then
		begin
		    repeat
		    until (not psns.req) or (psns.t_phase = msg_out_phase);
		end
		else
		begin
		    { tbd - handle synchronous xfer }
		end;
d1427 1
a1427 1
    if hwiManXfer(PtrSessionBlock,xfer_code,msg_out_phase,false) then
@


39.1
log
@Automatic bump of revision number for PWS version 3.23b
@
text
@@


1.1
log
@Initial revision
@
text
@@
