[comp.sys.mac.programmer] Simple instructional SCSI manager program wanted

name@Portia.Stanford.EDU (tony cooper) (12/28/88)

I would like to write a program that uses the SCSI manager. I have read about
this in Inside Mac vol IV and it looks simple enough. But I don't want to
risk trashing my disk drive by starting with a buggy program. Would someone
please send me an example of a program (C or Pascal) that makes SCSI calls
so I can see how it is done. Thanks.

Tony Cooper

name@stanford.EDU

ts@cup.portal.com (Tim W Smith) (01/07/89)

Here's a function that will show using the SCSI manager.  It is used
to perform 6 byte SCSI commands that will have a DATA IN phase.
 
SCSI6read( id, a, b, c, d, e, f, buf, len )
	int  id;
	char a, b, c, d, e, f;
	char * buf;
	int  len;
{
	char block[6];
	int  stat, mesg;
	SCSIInstr t[2];
	int	result;
	
	t[0].scOpcode = scNoInc;
	t[0].scParam1 = (long)buf;
	t[0].scParam2 = len;
	t[1].scOpcode = scStop;
	t[1].scParam1 = 0;
	t[1].scParam2 = 0;
	block[0] = a; block[1] = b; block[2] = c;
	block[3] = d; block[4] = e; block[5] = f;
	if ( SCSIGet() )
		return -1;
	if ( SCSISelect( id ) )
		return -2;
	if ( SCSICmd( block, 6 ) )
		return -3;
	result = SCSIRead(t);
	if ( SCSIComplete( &stat, &mesg, 180L ) )
		return -5;
	if ( result )
		return -4;
	return (stat<<8) | mesg;
}

Usage is result=SCSI6read( target_id, cmd_byte0, ... , cmd_byte5,
				buffer_pointer, buffer_length );

For example,
	SCSI6read( 0, 18, 0, 0, 0, 128, 0, buffer, 128 )
would fill memory at address "buffer" with the data returned by
an INQUIRY command on target 0.  Up to 128 bytes of inquiry data
would be returned.

result will be -1 to -5 if a SCSI Manager call fails, otherwise, it
will be the status and message returned by SCSIComplete.

Note that if one does a SCSICmd() that does not get an error, one must
at some point do SCSIComplete(), or the bus will be hung.  Thus, if
SCSIRead or SCSIWrite fails, you must still do the SCSIComplete.

						Tim Smith

ps: I have found a certain set of functions useful for my SCSI work.
Here is an overview:

	SCSI6read, SCSI6write, and SCSI6cmd are similar to the example
above, and are used for commands with DATA IN, DATA OUT, or no DATA phase.
However, these are not implemented like the above example.  Rather, they
build the command block and TIB and call the next function, SCSIdoCmd.

	SCSIdoCmd has this interface:

		result = SCSIdoCmd( cmdBuf, cmdLen, tibPtr, bufPtr,
			bufLen, dirFlag );
where cmdBuf is a pointer to the command block, cmdLen is the length of
the command block, tibPtr is a pointer to the TIB, or 0 if no DATA phase
is expected, bufPtr and bufLen specify a buffer ( only used if tibPtr !=
0 ), and dirFlag ( if tibPtr != 0 ) indicates the direction of data
transfer.

	If I need the 10 byte commands, then SCSI10{read,write,cmd} are
written.  They are similar to SCSI6{read,write,cmd}.

	If you want to get fancy, you can make SCSIdoCmd do an automatic
REQUEST SENSE when an error occurs, and store the result somewhere.  You
can even make it handle certain errors transparently ( for example, if the
command fails because of a UNIT ATTENTION condition, you can make SCSIdoCmd
retry the command ).

Write these functions once and make them into a LightspeedC library and
you can for the most part ignore the SCSI manager from then on.  Also, it's
a very good idea to go through some routines like this anyway, since Apple
is going to be changing the SCSI manager sometime "soon" ( according to
MACDTS ).  If all your SCSI calls go through a set of routines like this,
adpating to new SCSI Manager features will be easier.