/*

GKCONTROL.C

This program uses the SCSI generic class driver to send an inquiry command
to a device on the SCSI bus.
Alcita control version

This module implements the device setup and read/write needed
to control SCSI disks via the Alcita IDEplex board(s). It uses a
limited number of commands to do this control. It is presumed that
the device being controlled is indeed a disk of some kind.

*/

#include ctype

/* Define the descriptor used to pass the SCSI information to GKDRIVER */

/* following are offsets of longwords (or pointers in the case of addresses)
   in the 15-longword buffer */
/* 0 = SCSI operation code (cdrecord uses 1)
   1 = SCSI flags (bitmapped)
   2 = SCSI command buffer address
   3 = SCSI command buffer length, bytes
   4 = SCSI data buffer address
   5 = SCSI data length, bytes
   6 = SCSI pad length, bytes
   7 = SCSI phase change timeout (sec)
   8 = SCSI disconnect timeout (sec)
   9 = SCSI sense buffer address  (new drivers do request sense automatically)
  10 = SCSI sense buffer length
  11,12,13,14 = pad
*/

#define OPCODE 0
#define FLAGS 1
#define COMMAND_ADDRESS 2
#define COMMAND_LENGTH 3
#define DATA_ADDRESS 4
#define DATA_LENGTH 5
#define PAD_LENGTH 6
#define PHASE_TIMEOUT 7
#define DISCONNECT_TIMEOUT 8
#define SENSEADDR 9
#define SENSELEN 10

#define FLAGS_WRITE 0
#define FLAGS_READ 1
#define FLAGS_DISCONNECT 2
#define FLAGS_SYNCH 4
#define FLAGS_ASENSE 256

#define GK_EFN 1

#define SCSI_STATUS_MASK 0X3E

#define INQUIRY_OPCODE 0x12
#define INQUIRY_DATA_LENGTH 0x30

globalvalue
short
	gk_chan,
	transfer_length;

int
	i,
	status,
	gk_device_desc[2],
	gk_iosb[2],
	gk_desc[15];

char
	scsi_status,
	inquiry_cmd [6] = {18 , 0, 0, 0, 36, 0},
	test_unit_ready_cmd [6] = {0, 0, 0, 0, 0, 0},
	request_sense_cmd [6] = {3, 0, 0, 0, 18, 0},
	start_unit_cmd [6] = {27, 1, 0, 0, 1, 0},
	mode_sense_cmd [6] = {26, 0, 0x1, 0, 0x96, 0},
	read_capacity_cmd [10] = {37, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	mode_select_cmd [6] = {21 , 0x1, 0, 0, 23, 0},
	read_cmd [6] = {8, 0, 0, 0, 01, 0},
	write_cmd [6] = {10, 0, 0, 0, 0, 0},
	read10_cmd [10] = {0x28,0,0,0,0,0,0,0,01,0},
/* offset 2-5 is LBN, 7-8 is length in blocks */
	write10_cmd [10] = {0x2a,0,0,0,0,0,0,0,0,0},
	reassign_blocks_cmd [6] = {7, 0, 0, 0, 0, 0},
        rewind_command [6] = {1, 0, 0, 0, 0, 0},
	weof_command[6] = {16, 0, 0,0,1,0},
	inquiry_data[INQUIRY_DATA_LENGTH],
        mode_sel_data[23] = {0x00, 0x0, 0x0, 0x08, 0x00, 0x00, 0x00,
	0x00, 00, 00, 02, 00, 0x1, 0x0a, 0xE0, 03, 0x0, 0x0, 00, 03,
	0x0, 00, 0x0},
	gk_device[] = {"TARGET_DISK"};

char gbuf[512];
int dsk_setup(short chan, long *maxblk, int *cyl, int *sect, int *trks)
{
int i, ii;
long mxb;
/* spin up the disk and get size and make up geometry */
         i = setr_command(chan, start_unit_cmd, 6, gbuf, 0);
         if (i != 1) return(i);
	 i = setr_command(chan, test_unit_ready_cmd, 6, gbuf, 0);
         if (i != 1) return(i);
         i = setr_command(chan, read_capacity_cmd, 10, gbuf, 8);
         if (i != 1) return(i);
/* Should have device capacity in bigendian order here. Fix and
   save. */
         mxb=( gbuf[0] << 24) + (gbuf[1] << 16) +
                ( gbuf[2] <<8 ) + gbuf[3];
	 *maxblk = mxb;
         ii = gbuf[6] <<8 + gbuf[7];
         if (ii != 512) return 1;
/* compute geometry */
	 *trks = 6;
         *sect = 4;
         *cyl = mxb / 24;
         if ((mxb % 24) != 0) *cyl++;
         if (*cyl < 65534) return (1);
/* sector count too large ... try 32 by 32 by n */
         *trks = 32;
         *sect = 32;
         *cyl = mxb/1024;
         if ((mxb%1024) != 0) *cyl++;
         if (*cyl < 65534) return (1);
/* Too big for 32X32Xn so try 255X255Xn */
         *trks = 255;
         *sect = 255;
         *cyl = mxb / 65025;
         if ((mxb%65025) != 0) *cyl++;
/* if this won't fit, just force fit and use what can be used. */
         if (*cyl > 65535) *cyl = 65535;
         return (1);         
}
int setw_command (chan, cmd, cmd_len, indata, data_len, transfer_length){}

/* read block */
int read_gk_blk(short chan, char* buf, long bytes, long lbn, long * got)
{
   int i,ii;

/* First set up the SCSI command to read, then do the read. Use
   read(10) for simplicity. */
   ii = (bytes) / 512; /* number of blocks */
/* fill in transfer length in blocks */
/* Round DOWN; we do not want to overwrite! */
   read10_cmd[8] = (ii/256) & 255;
   read10_cmd[7] = (ii & 255); /* shift order for SCSI */
/* Fill in LBN */
   ii = lbn;
   read10_cmd[2] = (ii >>24) & 255;
   read10_cmd[3] = (ii >>16) & 255;
   read10_cmd[4] = (ii >>8)  & 255;
   read10_cmd[5] = (ii ) & 255;
   i = setr_command(chan, read10_cmd, 10, buf, bytes, got);
   return(i);
}

/* Write block */
int write_gk_blk(short chan, char* buf, long bytes, long lbn, long *bgot)
{
/* First set up the SCSI command to read, then do the read. Use
   write(10) for simplicity. */
   ii = (bytes) / 512; /* number of blocks */
/* fill in transfer length in blocks */
/* Round DOWN; we do not want to overwrite! */
   write10_cmd[8] = (ii/256) & 255;
   write10_cmd[7] = (ii & 255); /* shift order for SCSI */
/* Fill in LBN */
   ii = lbn;
   write10_cmd[2] = (ii >>24) & 255;
   write10_cmd[3] = (ii >>16) & 255;
   write10_cmd[4] = (ii >>8)  & 255;
   write10_cmd[5] = (ii ) & 255;
   i = setw_command(chan, write10_cmd, 10, buf, bytes, bgot);
   return(i);
}

/* Read type command sent to device to read data of data_len */
setr_command (gk_chan, cmd, cmd_len, obuf, data_len, got)
short gk_chan;
long *got;
char *cmd;
int cmd_len, data_len;
char obuf[512];
{
	char sensedata[512];

/* Set up the descriptor with the SCSI information to be sent to the target */
/*	for (i=0;i<511;i++)sensedata[i] = 0;*/

	gk_desc[OPCODE] = 1;
	gk_desc[FLAGS] = FLAGS_READ + FLAGS_SYNCH + FLAGS_ASENSE;
/* note FLAGS_DISCONNECT omitted from above */
	gk_desc[COMMAND_ADDRESS] = cmd;
	gk_desc[COMMAND_LENGTH] = cmd_len;
	gk_desc[DATA_ADDRESS] = obuf;
	gk_desc[DATA_LENGTH] = data_len;
	gk_desc[PAD_LENGTH] = 0;
	gk_desc[PHASE_TIMEOUT] = 10;
	gk_desc[DISCONNECT_TIMEOUT] = 10;
	for (i=9; i<15; i++) gk_desc[i] = 0;	/* Clear reserved fields */
        gk_desc[SENSEADDR] = sensedata;
	  gk_desc[SENSELEN] = 250; 
/* Issue the QIO to send the inquiry command and receive the inquiry data */

	status = sys$qiow (GK_EFN, gk_chan, IO$_DIAGNOSE, gk_iosb, 0, 0, 
			   &gk_desc[0], 15*4, 0, 0, 0, 0);

/* Check the various returned status values */

	if (!(status & 1)) return(status);
	if (!(gk_iosb[0] & 1)) return(4);
	scsi_status = (gk_iosb[1] >> 24) & SCSI_STATUS_MASK;
	if (scsi_status) {
           return(8);
	}
/* command worked. Return success. */
        *got = gk_iosb[0] >> 16;
       return(1);
}
int setw_command (chan, cmd, cmd_len, indata, data_len, transfer_length)
short chan;
char *cmd, *indata;
long * transfer_length;
int cmd_len, data_len;
{
	int kkk,kkkk;
	char *pk;
	char sensedata[512];

/*	for (i=0;i<511;i++)sensedata[i] = 0; */
	kkkk = 0;
/* Set up the descriptor with the SCSI information to be sent to the target */

	gk_desc[OPCODE] = 1;
/*	gk_desc[FLAGS] = FLAGS_WRITE + FLAGS_DISCONNECT + FLAGS_SYNCH + FLAGS_ASENSE; */
	gk_desc[FLAGS] = FLAGS_WRITE + FLAGS_SYNCH + FLAGS_ASENSE;
	gk_desc[COMMAND_ADDRESS] = cmd;
	gk_desc[COMMAND_LENGTH] = cmd_len;
	gk_desc[DATA_ADDRESS] = indata;
	gk_desc[DATA_LENGTH] = data_len;
	gk_desc[PAD_LENGTH] = 0;
	gk_desc[PHASE_TIMEOUT] = 10;
	gk_desc[DISCONNECT_TIMEOUT] = 10;
	for (i=9; i<15; i++) gk_desc[i] = 0;	/* Clear reserved fields */
/*        gk_desc[SENSEADDR] = sensedata;
	  gk_desc[SENSELEN] = 18; */

/* Issue the QIO to send the inquiry command and receive the inquiry data */

	status = sys$qiow (GK_EFN, gk_chan, IO$_DIAGNOSE, gk_iosb, 0, 0, 
			   &gk_desc[0], 15*4, 0, 0, 0, 0);

/* Check the various returned status values */

	if (!(status & 1)) return(status);
	if (!(gk_iosb[0] & 1)) return(4));
	scsi_status = (gk_iosb[1] >> 24) & SCSI_STATUS_MASK;
	if (scsi_status) {
                return(8);
	}

/* The command succeeded. */
	*transfer_length = gk_iosb[0] >> 16;
       return(1);
}
