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


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

56.2
date     93.01.27.12.25.29;  author jwh;  state Exp;
branches ;
next     56.1;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

43.1
date     90.03.20.14.15.26;  author jwh;  state Exp;
branches ;
next     42.3;

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

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

42.1
date     90.01.23.17.59.27;  author jwh;  state Exp;
branches ;
next     41.7;

41.7
date     90.01.19.09.15.30;  author dew;  state Exp;
branches ;
next     41.6;

41.6
date     90.01.18.22.55.41;  author dew;  state Exp;
branches ;
next     41.5;

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

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

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

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

41.1
date     89.12.22.11.41.15;  author jwh;  state Exp;
branches ;
next     40.6;

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

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

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

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

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

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

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

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


desc
@COMMANDS is SCSI Generic Driver Module.
@


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

	Contains the SCSI driver command level interface.
	This includes all actions as defined by the transport level, that
	is to: 1) ensure proper sequencing of sessions,
	       2) ensure proper transmission of commands, data, and status,
	       3) provide high level error recovery.
	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 routine DoSession
	ensures proper sequencing of the session, and starts off the next
	session with the state machine.

	the State Machine actually makes all of the calls, and the state
	tables enforce the division of labor (commands/messages) as defined
	above.
}
module COMMANDS;

import STATE_PROCS, SCSI_DEFS, SCSI_UTILS, STATE_DRIVE, OSD_LL, HWI_UTILS, MESSAGES;

export
	{
	 User's Interface Procedures
	}
	Procedure DoSession(PtrSessionBlock:PtrSessionBlockType);
	Procedure DoAbort(PtrSessionBlock:PtrSessionBlockType);
	Procedure DoReset(SelectCode:S_TYPE_ISC);

	{
	 State Machine Procedures
	}
	Procedure CmdGetStatus          (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure CmdTerminateSession   (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure CmdStateErr           (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure CmdPhaseErr           (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure CmdUnlinkAllSessions  (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);

	{
	 command utility routines.
	 Used by ISR when can't find a session to handle SCSI interrupt.
	}
	Procedure CmdSCCleanUp(Sc:S_TYPE_ISC);

implement



{*************************************************************************
 *  CmdSCUnlinkAllSessions
 *************************************************************************
 *
 * On Input:    Nothing Expected.
 *              Generally this routine is called when a castrophic error
 *              has occured and all SCSI activity must be cleaned up.
 *
 * Functional Description:
 *
 *      Unlink all of the session pointers for this select code.
 *      Set all found sessions to complete.
 *      If a session is waiting to be called back, call it back.
 *
 *
 * On Output:   There are no more Scsi Sessions on the bus for this
 *              Select Code.
 *
 *************************************************************************}
Procedure CmdSCUnlinkAllSessions(Sc:S_TYPE_ISC);
Var
	RunLevel:integer;
	PtrSessionBlock:PtrSessionBlockType;
	pScBlock:PtrSelectCodeBlockType;
	i:integer;
	DoAReset:Boolean;
	AllSessionsCleared:Boolean;
	MyMachine['AbortPath'] : integer;
begin
	RunLevel := osdGetRunlevel;
	osdSetRunLevel(7);
	try
		DoAReset := false;
		pScBlock := osdGetScBlock(Sc);
		with pScBlock^, PtrScsiCard^, PtrScsiChip^ do
		begin
		    sc_reg.ie := false; {disable interrupts, just in case.}
		    for i := 0 to 7 do
		    begin
			if DeviceSessionPtrs[i] <> Nil then
			begin
			    PtrSessionBlock := DeviceSessionPtrs[i];
			    repeat
				with PtrSessionBlock^ do
				begin
				    utlSetInternalErr(PtrSessionBlock, ScsiCatastrophicErr);
				    if (SessionState = SessionSuspended) then
					DoAReset := true
				    else if (SessionState = SessionRunning) then
				    begin
					init_exec_control(addr(InternalBlock.StateControlRec),
							  PtrSessionBlock, MyMachine, state_stack_size);
					init_trace(addr(InternalBlock.StateControlRec), 0, Nil);
					trace_control(addr(InternalBlock.StateControlRec), FALSE);
					exec(addr(InternalBlock.StateControlRec));
				    end;
				    SessionState := SessionComplete;
				    if (InternalBlock.CallBackLoaded) then
					call(SessionCompleteCallBack, PtrSessionBlock);
				    PtrSessionBlock := InternalBlock.NextSession;
				end;
			    until PtrSessionBlock = NIL;
			    DeviceSessionPtrs[i] := Nil;
			end;
		    end;
		    if DMAInProgress then
		    begin
			osdKillDMA(DMAChannel);
			osdReleaseDMAChannel(Sc);
			DMAInProgress := FALSE;
			config_reg.pal_reset := 1;
		    end;
		end;
	recover
		;
	if DoAReset then
		hwiSCHardReset(Sc);
	osdSetRunLevel(RunLevel);
end;


{*************************************************************************
 *  CmdScCleanUP
 *************************************************************************
 *
 * On Input:    Nothing expected.
 *              Called when everything on this Scsi Select Code needs to
 *              be cleaned up.
 *
 * Functional Description:
 *
 *      Reset the scsi bus.
 *      initialize the scsi hardware.
 *      Clean up any outstanding sessions.
 *
 * On Output:   Scsi Select Code is cleaned up.
 *
 *
 *************************************************************************}
Procedure CmdSCCleanUp(Sc:S_TYPE_ISC);
begin
	cmdSCUnlinkAllSessions(Sc);
	if osdGetScBlock(Sc)^.PtrScsiChip^.psns.bsy then
	begin
		hwiSCHardReset(Sc);
	end
	else
		hwiSCInitHW(Sc);
end;


{*************************************************************************
 * Procedure CmdInitSession
 *************************************************************************
 *
 * On Input:
 *
 *
 * Functional Description:
 *
 *
 *      Initialize the session block and internal block variables.
 *
 *      ensure that the inbound run level (isr level) will allow
 *      SCSI interrupts to occur.
 *
 *      Link this session block into the select code chain.
 *      In the select code block exists a session block pointer for each device.
 *      Link this session block in, and determine if this session can run or not.
 *
 *      Rule1: If another session block exists for this device, then link this
 *              session block in, and set the ResultCode to WaitForIsr.  It will
 *              be the responsibity of TerminateSession to start this session.
 *
 *      Rule2: If a session block exists for another device, and that session
 *              is Running or Suspended, then this session can not run (set ResultCode to WaitForISR).
 *              In this case, TerminateSession will start this
 *              session.
 *
 *      Rule3: If Rule1 or Rule2 have not been satisfied, then the session CAN run.
 *
 *
 * On Output:   NoEvent if the session can run.  ScsiErr if it can not.  If the
 *              Session is to be killed, then InternalStatus will be set, otherwise
 *              the session has been linked into SelectCodeBlock and will run
 *              when it can.
 *
 *************************************************************************}
Procedure CmdInitSession(PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
label
	1;
type
	tricky = record case boolean of
		true:(i:integer);
		false:(p:ScsiCallBackType);
	       end;
Var
	t:tricky;
	i:integer;
	locpSB:PtrSessionBlockType;

	procedure LocSetErr(ErrCode:InternalErrType);
	begin
		utlSetInternalErr(PtrSessionBlock, ErrCode);
		PtrSessionBlock^.SessionState := SessionComplete;
		ResultCode := ORD(ScsiErr);
	end;
begin
	with PtrSessionBlock^, PtrSessionBlock^.InternalBlock do
	begin
		{
		  Initialize the error fields.
		}
		InternalStatus := NoIntErr;
		SessionStatus := 0;

		{
		  Verify the session block is not being used.
		}
		if SessionState in [SessionRunning, SessionSuspended] then
		begin
			LocSetErr(ScsiStackingErr);
			goto 1;
		end;

		{
		  verify the session address parameters are correct.
		  If one or more are not, then ScsiAddressErr
		}
		pScBlock := osdGetScBlock(SelectCode);
		if (pScBlock = Nil) or (Device < 0) or (Device > 7)
		   or (LUN < 0) or (LUN > 7) or (SUN <> 0) then
		begin
			LocSetErr(ScsiAddressErr);
			goto 1;
		end;


		{
		  Initialize the session and internal blocks
		}
		SessionState := SessionWaiting;
		NextSession := NIL;
		ISRWaiting := False;
		SelectRetryCount := 0;
		t.p := SessionCompleteCallBack;
		CallBackLoaded := ((Overlap) and (t.i <> 0));
		SysCleanUp := cmdSCCleanUp;

		{
		  Make sure the incomming interrupt level is not to high.
		}
		with pScBlock^ do
		begin
			if osdGetRunLevel >= ScsiIntLevel then
			begin
				LocSetErr(RunLevelErr);
				goto 1;
			end;

			{
			  Check for Rule 1
			}
			locpSB := DeviceSessionPtrs[Device];
			if locpSB <> Nil then
			begin
				{
				  Link this SessionBlock into the SessionBlock chain.
				}
				if not Overlap then
				begin
					LocSetErr(ScsiStackingErr);
					goto 1;
				end;

				while locpSB^.InternalBlock.NextSession <> NIL do
					locpSB := locpSB^.InternalBlock.NextSession;

				locpSB^.InternalBlock.NextSession := PtrSessionBlock;
				ResultCode := ORD(WaitForISR);
				goto 1;
			end;

			{
			  CheckforRule 2
			}
			DeviceSessionPtrs[Device] := PtrSessionBlock;
			for i := 0 to 7 do
			if i <> Device then
			begin
				locpSB := DeviceSessionPtrs[i];
				if (locpSB <> Nil) and
				   (locpSB^.SessionState in [SessionRunning, SessionSuspended])
				   then
				begin
					if not Overlap then
					begin
						LocSetErr(ScsiStackingErr);
						goto 1;
					end;
					ResultCode := ORD(WaitForISR);
					goto 1;
				end;
			end;

			{
			  This session can Run.
			}
			ResultCode := ORD(NoEvent);
		end;
	end;
	1:
end;

{*************************************************************************
 *  DoSession
 *************************************************************************
 *
 * On Input:    SessionBlock has been filled out by user with Session
 *              required information.
 *
 *
 * Functional Description:
 *
 *      Called when session is to be started.
 *      Initialize the session block and call the state driver to kick
 *      things off.
 *
 *      If the session can not run, then either it is complete or
 *      it has been linked into the selectcode block and will run at
 *      a latter time.
 *
 * On Output:   SessionResults are inthe session block.
 *
 *************************************************************************}
Procedure DoSession(PtrSessionBlock:PtrSessionBlockType);
Var
	ResultCode:S_SHORT;
begin
	{
	  if an escape gets this far out, be sure to clean up.
	}
	with PtrSessionBlock^ do
	try
		CmdInitSession(PtrSessionBlock, ResultCode);
		if (ResultCode = ORD(NoEvent)) then
		begin
			StartState(PtrSessionBlock);
		end;
		if not Overlap then
			while SessionState <> SessionComplete do
				;
	recover
	begin
		CmdSCCleanUp(SelectCode);
		escape(escapecode);
	end;
end;


{*************************************************************************
 *  DoAbort
 *************************************************************************
 *
 * On Input:    SessionBlock comes from user.  The session is overlapped.
 *      The session was active at the time of the call, but they may have
 *      changed.
 *
 * Functional Description:
 *
 *      set interrupt level so that SCSI can no longer interrupt
 *      and make sure that the session is still active.
 *      Set ISRError to TRUE and restart the session, This will
 *      force the driver to attempt to send an ABORT message, and
 *      if that fails, reset the bus.
 *
 * On Output:
 *
 *************************************************************************}
Procedure DoAbort(PtrSessionBlock:PtrSessionBlockType);
var
	SaveLevel:integer;
begin
	with PtrSessionBlock^, InternalBlock, pScBlock^ do
	begin
		{
		  set interrupt level
		}
		SaveLevel := osdGetRunLevel;
		if SaveLevel < ScsiIntLevel then
			osdSetRunLevel(ScsiIntLevel)
		else
			SaveLevel := 100; {larger than the largest interupt level}


		if SessionState = SessionRunning then
		begin
			ISRError := TRUE;
			StartState(PtrSessionBlock);
		end;


		{
		  reset interrupt level
		}
		if SaveLevel < ScsiIntLevel then
			osdSetRunLevel(SaveLevel);
	end;
end;

{*************************************************************************
 *  DoReset
 *************************************************************************
 *
 * On Input:    Nothing is known.
 *
 *
 * Functional Description:
 *
 *   Verify the select code is indeed a SCSI select code and then
 *   reset the select code and the bus.
 *
 * On Output:
 *
 *************************************************************************}
Procedure DoReset(SelectCode:S_TYPE_ISC);
begin
	if osdGetScBlock(SelectCode) <> NIL then
	begin
		cmdSCUnlinkAllSessions(SelectCode);
		hwiSCHardReset(SelectCode);
	end;
end;

{*************************************************************************
 *  CmdGetStatus
 *************************************************************************
 *
 * On Input:    Bus is in Status Phase
 *
 *
 * Functional Description:
 *
 *      Get the status from the target and set it in the SessionStatus
 *      variable in the session block.
 *
 *      This routine will also set the ResidualCount to what is in the
 *      XferBlock.
 *
 * On Output:   NoEvent or ScsiErr if xfer error has occured.
 *
 *
 *************************************************************************}
Procedure CmdGetStatus(PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
  xfer_code : s_byte;

begin
    if hwiManXfer(PtrSessionBlock,xfer_code,status_phase,false,false) then
    begin
      ResultCode := ORD(NoEvent);
      with PtrSessionBlock^ do
      begin
	  SessionStatus := xfer_code;
	  ResidualCount := InternalBlock.XferBlock.XferBufBlock.BufLen;
      end;
    end
    else
    begin
      ResultCode := ORD(ScsiErr);
      utlSetInternalErr(PtrSessionBlock, ScsiXferErr);
    end;
end;


{*************************************************************************
 *  CmdTerminateSession
 *************************************************************************
 *
 * On Input:    The session is to be properly terminated.
 *
 *
 * Functional Description:
 *
 *      Terminate the session by setting it to complete and taking it out
 *      of the select code block.  If a call back routine is to be called,
 *      do it now.
 *
 *      Look in the select code block for sessions that are waiting to be
 *      started.
 *
 * On Output:   NoEvent
 *
 *
 *************************************************************************}
Procedure CmdTerminateSession(PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
	Idx:integer;
	Done:boolean;
	locpSB:PtrSessionBlockType;
begin
	ResultCode := ORD(NoEvent);
	with PtrSessionBlock^, InternalBlock, pScBlock^ do
	begin
		if (CallBackLoaded) then
			call(SessionCompleteCallBack, PtrSessionBlock);
		SessionState := SessionComplete;

		DeviceSessionPtrs[Device] := NextSession;

		Idx := Device;
		Done := FALSE;
		while not Done do
		begin
			if Idx = 7 then
				Idx := 0
			else
				Idx := Idx + 1;
			locpSB := DeviceSessionPtrs[Idx];
			if (locpSB <> Nil) and (locpSB^.SessionState = SessionWaiting) then
			begin
				Done := TRUE;
				StartState(locpSB);
			end
			else if Idx = Device then
				Done := TRUE;
		end;
	end;
end;

{*************************************************************************
 *  CmdStateErr
 *************************************************************************
 *
 * On Input:    The state machine has detected an internal error type of
 *              no event found in event/nextstate list or escape error.
 *
 *
 * Functional Description:
 *
 *      Determine the state machine error type and log it appropirately in
 *      the session block.
 *
 * On Output:   NoEvent
 *
 *
 *************************************************************************}
Procedure CmdStateErr(PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	utlSetInternalErr(PtrSessionBlock, StateMachineErr);
	ResultCode := Ord(NoEvent);
end;

{*************************************************************************
 *  CmdPhaseErr
 *************************************************************************
 *
 * On Input:    The current bus phase is not anticipated.
 *
 *
 * Functional Description:
 *
 *      Set internal error to bus phase error
 *
 *
 *
 *
 * On Output:   NoEvent
 *
 *
 *************************************************************************}
Procedure CmdPhaseErr(PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	utlSetInternalErr(PtrSessionBlock, ScsiPhaseErr);
	ResultCode := Ord(NoEvent);
end;

{*************************************************************************
 *  CmdUnlinkAllSessions
 *************************************************************************
 *
 * On Input:    State Machine is handling a catastrophic error.
 *
 *
 * Functional Description:
 *
 *      Unlink all sessions by call cmdScUnlinkAllSessions
 *
 * On Output:   NoEvent
 *
 *
 *************************************************************************}
Procedure CmdUnlinkAllSessions(PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	cmdSCUnlinkAllSessions(PtrSessionBlock^.SelectCode);
	ResultCode := Ord(NoEvent);
end;



end;
@


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


56.1
log
@Automatic bump of revision number for PWS version 3.25
@
text
@a0 611
{
	SCSI Command Module.

	Contains the SCSI driver command level interface.
	This includes all actions as defined by the transport level, that
	is to: 1) ensure proper sequencing of sessions,
	       2) ensure proper transmission of commands, data, and status,
	       3) provide high level error recovery.
	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 routine DoSession
	ensures proper sequencing of the session, and starts off the next
	session with the state machine.

	the State Machine actually makes all of the calls, and the state
	tables enforce the division of labor (commands/messages) as defined
	above.
}
module COMMANDS;

import STATE_PROCS, SCSI_DEFS, SCSI_UTILS, STATE_DRIVE, OSD_LL, HWI_UTILS, MESSAGES;

export
	{
	 User's Interface Procedures
	}
	Procedure DoSession(PtrSessionBlock:PtrSessionBlockType);
	Procedure DoAbort(PtrSessionBlock:PtrSessionBlockType);
	Procedure DoReset(SelectCode:S_TYPE_ISC);

	{
	 State Machine Procedures
	}
	Procedure CmdGetStatus          (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure CmdTerminateSession   (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure CmdStateErr           (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure CmdPhaseErr           (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
	Procedure CmdUnlinkAllSessions  (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);

	{
	 command utility routines.
	 Used by ISR when can't find a session to handle SCSI interrupt.
	}
	Procedure CmdSCCleanUp(Sc:S_TYPE_ISC);

implement



{*************************************************************************
 *  CmdSCUnlinkAllSessions
 *************************************************************************
 *
 * On Input:    Nothing Expected.
 *              Generally this routine is called when a castrophic error
 *              has occured and all SCSI activity must be cleaned up.
 *
 * Functional Description:
 *
 *      Unlink all of the session pointers for this select code.
 *      Set all found sessions to complete.
 *      If a session is waiting to be called back, call it back.
 *
 *
 * On Output:   There are no more Scsi Sessions on the bus for this
 *              Select Code.
 *
 *************************************************************************}
Procedure CmdSCUnlinkAllSessions(Sc:S_TYPE_ISC);
Var
	RunLevel:integer;
	PtrSessionBlock:PtrSessionBlockType;
	pScBlock:PtrSelectCodeBlockType;
	i:integer;
	DoAReset:Boolean;
	AllSessionsCleared:Boolean;
	MyMachine['AbortPath'] : integer;
begin
	RunLevel := osdGetRunlevel;
	osdSetRunLevel(7);
	try
		DoAReset := false;
		pScBlock := osdGetScBlock(Sc);
		with pScBlock^, PtrScsiCard^, PtrScsiChip^ do
		begin
		    sc_reg.ie := false; {disable interrupts, just in case.}
		    for i := 0 to 7 do
		    begin
			if DeviceSessionPtrs[i] <> Nil then
			begin
			    PtrSessionBlock := DeviceSessionPtrs[i];
			    repeat
				with PtrSessionBlock^ do
				begin
				    utlSetInternalErr(PtrSessionBlock, ScsiCatastrophicErr);
				    if (SessionState = SessionSuspended) then
					DoAReset := true
				    else if (SessionState = SessionRunning) then
				    begin
					init_exec_control(addr(InternalBlock.StateControlRec),
							  PtrSessionBlock, MyMachine, state_stack_size);
					init_trace(addr(InternalBlock.StateControlRec), 0, Nil);
					trace_control(addr(InternalBlock.StateControlRec), FALSE);
					exec(addr(InternalBlock.StateControlRec));
				    end;
				    SessionState := SessionComplete;
				    if (InternalBlock.CallBackLoaded) then
					call(SessionCompleteCallBack, PtrSessionBlock);
				    PtrSessionBlock := InternalBlock.NextSession;
				end;
			    until PtrSessionBlock = NIL;
			    DeviceSessionPtrs[i] := Nil;
			end;
		    end;
		    if DMAInProgress then
		    begin
			osdKillDMA(DMAChannel);
			osdReleaseDMAChannel(Sc);
			DMAInProgress := FALSE;
			config_reg.pal_reset := 1;
		    end;
		end;
	recover
		;
	if DoAReset then
		hwiSCHardReset(Sc);
	osdSetRunLevel(RunLevel);
end;


{*************************************************************************
 *  CmdScCleanUP
 *************************************************************************
 *
 * On Input:    Nothing expected.
 *              Called when everything on this Scsi Select Code needs to
 *              be cleaned up.
 *
 * Functional Description:
 *
 *      Reset the scsi bus.
 *      initialize the scsi hardware.
 *      Clean up any outstanding sessions.
 *
 * On Output:   Scsi Select Code is cleaned up.
 *
 *
 *************************************************************************}
Procedure CmdSCCleanUp(Sc:S_TYPE_ISC);
begin
	cmdSCUnlinkAllSessions(Sc);
	if osdGetScBlock(Sc)^.PtrScsiChip^.psns.bsy then
	begin
		hwiSCHardReset(Sc);
	end
	else
		hwiSCInitHW(Sc);
end;


{*************************************************************************
 * Procedure CmdInitSession
 *************************************************************************
 *
 * On Input:
 *
 *
 * Functional Description:
 *
 *
 *      Initialize the session block and internal block variables.
 *
 *      ensure that the inbound run level (isr level) will allow
 *      SCSI interrupts to occur.
 *
 *      Link this session block into the select code chain.
 *      In the select code block exists a session block pointer for each device.
 *      Link this session block in, and determine if this session can run or not.
 *
 *      Rule1: If another session block exists for this device, then link this
 *              session block in, and set the ResultCode to WaitForIsr.  It will
 *              be the responsibity of TerminateSession to start this session.
 *
 *      Rule2: If a session block exists for another device, and that session
 *              is Running or Suspended, then this session can not run (set ResultCode to WaitForISR).
 *              In this case, TerminateSession will start this
 *              session.
 *
 *      Rule3: If Rule1 or Rule2 have not been satisfied, then the session CAN run.
 *
 *
 * On Output:   NoEvent if the session can run.  ScsiErr if it can not.  If the
 *              Session is to be killed, then InternalStatus will be set, otherwise
 *              the session has been linked into SelectCodeBlock and will run
 *              when it can.
 *
 *************************************************************************}
Procedure CmdInitSession(PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
label
	1;
type
	tricky = record case boolean of
		true:(i:integer);
		false:(p:ScsiCallBackType);
	       end;
Var
	t:tricky;
	i:integer;
	locpSB:PtrSessionBlockType;

	procedure LocSetErr(ErrCode:InternalErrType);
	begin
		utlSetInternalErr(PtrSessionBlock, ErrCode);
		PtrSessionBlock^.SessionState := SessionComplete;
		ResultCode := ORD(ScsiErr);
	end;
begin
	with PtrSessionBlock^, PtrSessionBlock^.InternalBlock do
	begin
		{
		  Initialize the error fields.
		}
		InternalStatus := NoIntErr;
		SessionStatus := 0;

		{
		  Verify the session block is not being used.
		}
		if SessionState in [SessionRunning, SessionSuspended] then
		begin
			LocSetErr(ScsiStackingErr);
			goto 1;
		end;

		{
		  verify the session address parameters are correct.
		  If one or more are not, then ScsiAddressErr
		}
		pScBlock := osdGetScBlock(SelectCode);
		if (pScBlock = Nil) or (Device < 0) or (Device > 7)
		   or (LUN < 0) or (LUN > 7) or (SUN <> 0) then
		begin
			LocSetErr(ScsiAddressErr);
			goto 1;
		end;


		{
		  Initialize the session and internal blocks
		}
		SessionState := SessionWaiting;
		NextSession := NIL;
		ISRWaiting := False;
		SelectRetryCount := 0;
		t.p := SessionCompleteCallBack;
		CallBackLoaded := ((Overlap) and (t.i <> 0));
		SysCleanUp := cmdSCCleanUp;

		{
		  Make sure the incomming interrupt level is not to high.
		}
		with pScBlock^ do
		begin
			if osdGetRunLevel >= ScsiIntLevel then
			begin
				LocSetErr(RunLevelErr);
				goto 1;
			end;

			{
			  Check for Rule 1
			}
			locpSB := DeviceSessionPtrs[Device];
			if locpSB <> Nil then
			begin
				{
				  Link this SessionBlock into the SessionBlock chain.
				}
				if not Overlap then
				begin
					LocSetErr(ScsiStackingErr);
					goto 1;
				end;

				while locpSB^.InternalBlock.NextSession <> NIL do
					locpSB := locpSB^.InternalBlock.NextSession;

				locpSB^.InternalBlock.NextSession := PtrSessionBlock;
				ResultCode := ORD(WaitForISR);
				goto 1;
			end;

			{
			  CheckforRule 2
			}
			DeviceSessionPtrs[Device] := PtrSessionBlock;
			for i := 0 to 7 do
			if i <> Device then
			begin
				locpSB := DeviceSessionPtrs[i];
				if (locpSB <> Nil) and
				   (locpSB^.SessionState in [SessionRunning, SessionSuspended])
				   then
				begin
					if not Overlap then
					begin
						LocSetErr(ScsiStackingErr);
						goto 1;
					end;
					ResultCode := ORD(WaitForISR);
					goto 1;
				end;
			end;

			{
			  This session can Run.
			}
			ResultCode := ORD(NoEvent);
		end;
	end;
	1:
end;

{*************************************************************************
 *  DoSession
 *************************************************************************
 *
 * On Input:    SessionBlock has been filled out by user with Session
 *              required information.
 *
 *
 * Functional Description:
 *
 *      Called when session is to be started.
 *      Initialize the session block and call the state driver to kick
 *      things off.
 *
 *      If the session can not run, then either it is complete or
 *      it has been linked into the selectcode block and will run at
 *      a latter time.
 *
 * On Output:   SessionResults are inthe session block.
 *
 *************************************************************************}
Procedure DoSession(PtrSessionBlock:PtrSessionBlockType);
Var
	ResultCode:S_SHORT;
begin
	{
	  if an escape gets this far out, be sure to clean up.
	}
	with PtrSessionBlock^ do
	try
		CmdInitSession(PtrSessionBlock, ResultCode);
		if (ResultCode = ORD(NoEvent)) then
		begin
			StartState(PtrSessionBlock);
		end;
		if not Overlap then
			while SessionState <> SessionComplete do
				;
	recover
	begin
		CmdSCCleanUp(SelectCode);
		escape(escapecode);
	end;
end;


{*************************************************************************
 *  DoAbort
 *************************************************************************
 *
 * On Input:    SessionBlock comes from user.  The session is overlapped.
 *      The session was active at the time of the call, but they may have
 *      changed.
 *
 * Functional Description:
 *
 *      set interrupt level so that SCSI can no longer interrupt
 *      and make sure that the session is still active.
 *      Set ISRError to TRUE and restart the session, This will
 *      force the driver to attempt to send an ABORT message, and
 *      if that fails, reset the bus.
 *
 * On Output:
 *
 *************************************************************************}
Procedure DoAbort(PtrSessionBlock:PtrSessionBlockType);
var
	SaveLevel:integer;
begin
	with PtrSessionBlock^, InternalBlock, pScBlock^ do
	begin
		{
		  set interrupt level
		}
		SaveLevel := osdGetRunLevel;
		if SaveLevel < ScsiIntLevel then
			osdSetRunLevel(ScsiIntLevel)
		else
			SaveLevel := 100; {larger than the largest interupt level}


		if SessionState = SessionRunning then
		begin
			ISRError := TRUE;
			StartState(PtrSessionBlock);
		end;


		{
		  reset interrupt level
		}
		if SaveLevel < ScsiIntLevel then
			osdSetRunLevel(SaveLevel);
	end;
end;

{*************************************************************************
 *  DoReset
 *************************************************************************
 *
 * On Input:    Nothing is known.
 *
 *
 * Functional Description:
 *
 *   Verify the select code is indeed a SCSI select code and then
 *   reset the select code and the bus.
 *
 * On Output:
 *
 *************************************************************************}
Procedure DoReset(SelectCode:S_TYPE_ISC);
begin
	if osdGetScBlock(SelectCode) <> NIL then
	begin
		cmdSCUnlinkAllSessions(SelectCode);
		hwiSCHardReset(SelectCode);
	end;
end;

{*************************************************************************
 *  CmdGetStatus
 *************************************************************************
 *
 * On Input:    Bus is in Status Phase
 *
 *
 * Functional Description:
 *
 *      Get the status from the target and set it in the SessionStatus
 *      variable in the session block.
 *
 *      This routine will also set the ResidualCount to what is in the
 *      XferBlock.
 *
 * On Output:   NoEvent or ScsiErr if xfer error has occured.
 *
 *
 *************************************************************************}
Procedure CmdGetStatus(PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
  xfer_code : s_byte;

begin
    if hwiManXfer(PtrSessionBlock,xfer_code,status_phase,false,false) then
    begin
      ResultCode := ORD(NoEvent);
      with PtrSessionBlock^ do
      begin
	  SessionStatus := xfer_code;
	  ResidualCount := InternalBlock.XferBlock.XferBufBlock.BufLen;
      end;
    end
    else
    begin
      ResultCode := ORD(ScsiErr);
      utlSetInternalErr(PtrSessionBlock, ScsiXferErr);
    end;
end;


{*************************************************************************
 *  CmdTerminateSession
 *************************************************************************
 *
 * On Input:    The session is to be properly terminated.
 *
 *
 * Functional Description:
 *
 *      Terminate the session by setting it to complete and taking it out
 *      of the select code block.  If a call back routine is to be called,
 *      do it now.
 *
 *      Look in the select code block for sessions that are waiting to be
 *      started.
 *
 * On Output:   NoEvent
 *
 *
 *************************************************************************}
Procedure CmdTerminateSession(PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
var
	Idx:integer;
	Done:boolean;
	locpSB:PtrSessionBlockType;
begin
	ResultCode := ORD(NoEvent);
	with PtrSessionBlock^, InternalBlock, pScBlock^ do
	begin
		if (CallBackLoaded) then
			call(SessionCompleteCallBack, PtrSessionBlock);
		SessionState := SessionComplete;

		DeviceSessionPtrs[Device] := NextSession;

		Idx := Device;
		Done := FALSE;
		while not Done do
		begin
			if Idx = 7 then
				Idx := 0
			else
				Idx := Idx + 1;
			locpSB := DeviceSessionPtrs[Idx];
			if (locpSB <> Nil) and (locpSB^.SessionState = SessionWaiting) then
			begin
				Done := TRUE;
				StartState(locpSB);
			end
			else if Idx = Device then
				Done := TRUE;
		end;
	end;
end;

{*************************************************************************
 *  CmdStateErr
 *************************************************************************
 *
 * On Input:    The state machine has detected an internal error type of
 *              no event found in event/nextstate list or escape error.
 *
 *
 * Functional Description:
 *
 *      Determine the state machine error type and log it appropirately in
 *      the session block.
 *
 * On Output:   NoEvent
 *
 *
 *************************************************************************}
Procedure CmdStateErr(PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	utlSetInternalErr(PtrSessionBlock, StateMachineErr);
	ResultCode := Ord(NoEvent);
end;

{*************************************************************************
 *  CmdPhaseErr
 *************************************************************************
 *
 * On Input:    The current bus phase is not anticipated.
 *
 *
 * Functional Description:
 *
 *      Set internal error to bus phase error
 *
 *
 *
 *
 * On Output:   NoEvent
 *
 *
 *************************************************************************}
Procedure CmdPhaseErr(PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	utlSetInternalErr(PtrSessionBlock, ScsiPhaseErr);
	ResultCode := Ord(NoEvent);
end;

{*************************************************************************
 *  CmdUnlinkAllSessions
 *************************************************************************
 *
 * On Input:    State Machine is handling a catastrophic error.
 *
 *
 * Functional Description:
 *
 *      Unlink all sessions by call cmdScUnlinkAllSessions
 *
 * On Output:   NoEvent
 *
 *
 *************************************************************************}
Procedure CmdUnlinkAllSessions(PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	cmdSCUnlinkAllSessions(PtrSessionBlock^.SelectCode);
	ResultCode := Ord(NoEvent);
end;



end;
@


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
@
CmdSCUnlinkAllSessions:

	rewritten to repair defects associated with STOP key processing.
	if session is running, start state machine with the AbortPath machine.
	if session is suspended, reset the bus - BUT NOT UNTIL ALL SESSIONS HAVE
	BEEN EXAMINED.
	make sure that interrupt level is at 7 - do not want an interrupt or an
	escape during this critical code.

CmdSCCleanUp:

	call Unlink sessions, then initialize or reset H/W.

DoSession:

	trap escapes at this outermost level.  Call cmdScCleanUp if escape occurs.
@
text
@@


45.1
log
@Automatic bump of revision number for PWS version 3.23C
@
text
@d22 1
a22 1
import SCSI_DEFS, SCSI_UTILS, STATE_DRIVE, OSD_LL, HWI_UTILS, MESSAGES;
d72 1
d76 1
a76 1
	DoneAReset:Boolean;
d78 1
d80 6
a85 5
	DoneAReset := false;
	pScBlock := osdGetScBlock(Sc);
	with pScBlock^ do
	begin
		if DMAInProgress then
d87 31
d121 2
a122 1
			PtrScsiCard^.config_reg.pal_reset := 1;
d124 5
a128 25
		for i := 0 to 7 do
		begin
			if DeviceSessionPtrs[i] <> Nil then
			begin
				PtrSessionBlock := DeviceSessionPtrs[i];
				repeat
					with PtrSessionBlock^ do
					begin
						utlSetInternalErr(PtrSessionBlock, ScsiCatastrophicErr);
						if (SessionState = SessionSuspended) and
						   (not DoneAReset) then
						begin
							hwiScHardReset(Sc);
							DoneAReset := true;
						end;
						SessionState := SessionComplete;
						if (InternalBlock.CallBackLoaded) then
							call(SessionCompleteCallBack, PtrSessionBlock);
						PtrSessionBlock := InternalBlock.NextSession;
					end;
				until PtrSessionBlock = NIL;
				DeviceSessionPtrs[i] := Nil;
			end;
		end;
	end;
d152 1
d154 3
a156 1
		hwiSCHardReset(Sc)
a158 1
	cmdSCUnlinkAllSessions(Sc);
d350 3
a352 5
	CmdInitSession(PtrSessionBlock, ResultCode);
	if (ResultCode = ORD(NoEvent)) then
	begin
		StartState(PtrSessionBlock);
	end;
d354 6
d363 5
d440 1
a441 1
		cmdSCUnlinkAllSessions(SelectCode);
@


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.3
log
@Added better error checking to the SessionBlock parameters in
CmdInitSession.
@
text
@@


42.2
log
@Took out cmdGetLinkedCommand as support for SCSI linked commands
has been dropped.
@
text
@d205 1
a205 1
		  Initialize the session and internal blocks
a208 8
		SessionState := SessionWaiting;
		NextSession := NIL;
		ISRWaiting := False;
		SelectRetryCount := 0;
		t.p := SessionCompleteCallBack;
		CallBackLoaded := (t.i <> 0);
		SysCleanUp := cmdSCCleanUp;
		pScBlock := osdGetScBlock(SelectCode);
d210 8
d220 2
a221 2
		  verify the pScBlock is correct.
		  If it isn't, then the caller has given us a bad select code.
d223 3
a225 1
		if (pScBlock = Nil) then
d231 1
d233 11
a488 1
		SessionState := SessionComplete;
d491 1
@


42.1
log
@Automatic bump of revision number for PWS version 3.23e
@
text
@a35 1
	Procedure CmdGetLinkedCommand   (PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
a444 22
{*************************************************************************
 *  CmdGetLinkedCommand
 *************************************************************************
 *
 * On Input:
 *
 *
 * Functional Description:
 *
 *      To Be Implemented later.
 *
 *
 *
 *
 * On Output:
 *
 *
 *************************************************************************}
Procedure CmdGetLinkedCommand(PtrSessionBlock:PtrSessionBlockType; Var ResultCode:S_SHORT);
begin
	ResultCode := ORD(ScsiErr);
end;
@


41.7
log
@Only abort sessions if they are in the SessionRunning state.
@
text
@@


41.6
log
@Fixed problem with improrper unstacking of sessions during 
cmdScUnlinkAllSessions.

Added DoAbort

Added DoReset
@
text
@d367 1
a367 1
		if SessionState <> SessionComplete then
@


41.5
log
@Added stacked commands support.  This required a rewrite of CmdInitSession and additions
to CmdTerminateSession.
@
text
@d29 2
d53 1
a53 1
 *  CmdSCUnlinkAllSessoins
d77 1
d95 2
a96 5
				DeviceSessionPtrs[i] := Nil;
				with PtrSessionBlock^ do
				begin
					utlSetInternalErr(PtrSessionBlock, ScsiCatastrophicErr);
					if (SessionState = SessionSuspended) and (not DoneAReset) then
d98 11
a108 2
						hwiScHardReset(Sc);
						DoneAReset := true;
d110 2
a111 4
					SessionState := SessionComplete;
					if (InternalBlock.CallBackLoaded) then
						call(SessionCompleteCallBack, PtrSessionBlock);
				end;
d329 75
@


41.4
log
@
When a select code is being cleaned up, i.e. stop key has been pressed,
and a suspended session has been found, then reset the bus.  This will
guarantee the peripheral is cleaned up and that no unattached ISRs will
occur.

@
text
@d163 2
a164 2
 *              is Running, then this session can not run (set ResultCode to WaitForISR).
 *              In this case, either TerminateSession or WaitReselect will start this
d177 2
d186 2
d189 6
d204 1
d219 8
a226 5
			utlSetInternalErr(PtrSessionBlock, ScsiAddressErr);
			SessionState := SessionComplete;
			ResultCode := ORD(ScsiErr);
		end
		else
d228 6
d235 1
a235 1
			  Make sure the incomming interrupt level is not to high.
d237 2
a238 2
			with pScBlock^ do
			if osdGetRunLevel >= ScsiIntLevel then
a239 6
				utlSetInternalErr(PtrSessionBlock, RunLevelErr);
				SessionState := SessionComplete;
				ResultCode := ORD(ScsiErr);
			end
			else
			begin
d241 1
a241 1
				  Link this session block into the select code chain.
d243 5
d249 19
a267 2
				{ the above rules are tbd.  For now, link, and set state to running. }
				if DeviceSessionPtrs[Device] <> Nil then
d269 7
a275 8
					utlSetInternalErr(PtrSessionBlock, tbderr);
					SessionState := SessionComplete;
					ResultCode := ORD(ScsiErr);
				end
				else
				begin
					DeviceSessionPtrs[Device] := PtrSessionBlock;
					ResultCode := ORD(NoEvent);
d278 5
d285 1
d408 4
d420 19
a438 2
		{ tbd need to check for sessions to start. for now just clear sessionblocktable}
		DeviceSessionPtrs[Device] := Nil;
a460 1
	{tbd set up the correct error type}
@


41.3
log
@SCHardReset now also initializes the H/W.  This was required
because these routines had different codes for setting up sctl, when
originally they were the same.  Thus, sctl should be set up in one
place. Therefore, HardReset now calls InitHW which sets up sctl.
@
text
@d74 1
d76 1
d96 5
@


41.2
log
@When calling the SessionCompleteCallBack procedure, include a pointer
to the SessionBlock in the parameter list.
@
text
@d125 3
a127 2
		hwiSCHardReset(Sc);
	hwiSCInitHW(Sc);
@


41.1
log
@Automatic bump of revision number for PWS version 3.23d
@
text
@d96 1
a96 1
						call(SessionCompleteCallBack);
d172 1
a172 1
		false:(p:procedure);
d365 1
a365 1
			call(SessionCompleteCallBack);
@


40.6
log
@Make sure select code block is really there before using it!
@
text
@@


40.5
log
@Updated to match the modified osdKillDMA interface.
@
text
@d193 1
d195 2
a196 1
		  Make sure the incomming interrupt level is not to high.
d198 1
a198 2
		with pScBlock^ do
		if osdGetRunLevel >= ScsiIntLevel then
d200 1
a200 1
			utlSetInternalErr(PtrSessionBlock, RunLevelErr);
d207 1
a207 1
			  Link this session block into the select code chain.
d209 2
a210 3

			{ the above rules are tbd.  For now, link, and set state to running. }
			if DeviceSessionPtrs[Device] <> Nil then
d212 1
a212 1
				utlSetInternalErr(PtrSessionBlock, tbderr);
d218 16
a233 2
				DeviceSessionPtrs[Device] := PtrSessionBlock;
				ResultCode := ORD(NoEvent);
@


40.4
log
@Modifications for DMA support.
@
text
@d80 1
a80 1
			osdKillDMA(DMAChannel, DMA32bit);
@


40.3
log
@Updated to reflect SCSI_DEFS modifications for programmer's interface.
@
text
@d78 7
@


40.2
log
@Modifications to support synchronous protocol, and to support
the new hwiManXfer interface
@
text
@d87 1
a87 1
					InternalBlock.SessionState := SessionComplete;
d248 1
a248 1
	with PtrSessionBlock^, PtrSessionBlock^.InternalBlock do
d340 1
a340 1
	with PtrSessionBlock^, PtrSessionBlock^.InternalBlock, PtrSessionBlock^.InternalBlock.pScBlock^ do
@


40.1
log
@Automatic bump of revision number for PWS version 3.23c
@
text
@a79 1
			DeviceSynchParms[i].DeviceSynched := false;
d278 1
a278 1
    if hwiManXfer(PtrSessionBlock,xfer_code,status_phase,false) then
@


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


1.1
log
@Initial revision
@
text
@@
