/*
This program originated as a demo of using IO$_DIAGNOSE
but has been morphed into a tool.
  It is designed to be used with an Exabyte 8500 series tape
assigned as TARGET_TAPE and writes what looks like a short empty
Backup saveset to the tape at Exabyte 8200 mode density. Thus the
tape appears to have been INITed by backup on an 8200 and can
be appended to.

Thus one's commands might look like this:

$ assign mkb500: target_tape
$ mount/foreign mkb500:
$ run gkinit
 (lots of verbose output)
$ dism mkb500:
$ back/list/ign=label mkb500:

%MOUNT-I-MOUNTED, EX8200 mounted on MKB500:

End of save set

$ back/ign=label disk:[*...] mkb500:disksav.bck

...

$ dism mkb500:

The resulting tape will look like an 8200 tape, except for the short
initial saveset and the fact that the label will always be EX8200.

No MKdriver mods are needed for this.
*/
/*

gkinit.C

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

*/

#include ctype

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

#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
	IO$_DIAGNOSE;

/* fake tape init blocks*/
/* Idea here is to "init" an exabyte 8500 in exabyte 8200 mode
   where that is needed by writing the records by hand. */
/* These are obtained from a tape dump after the command
  ini/over=(exp,own,acc) mkb500: ex820
   was issued. */

	int taprecs[60]=
     {0x314C4F56, 0x32385845, 0x20203030, 0x20202020,
      0x20202020, 0x20202020, 0x20202020, 0x20202020,
      0x20202020, 0x20202020, 0x20202020, 0x20202020,
      0x20202020, 0x20202020, 0x20202020, 0x20202020,
      0x20202020, 0x20202020, 0x20202020, 0x33202020,
      0x31524448, 0x32385845, 0x202E3030, 0x20202020,
      0x20202020, 0x38584520, 0x30303032, 0x30313030,
      0x30313030, 0x30313030, 0x35392030, 0x20333731,
      0x37313539, 0x30302033, 0x30303030, 0x56434544,
      0x4142534D, 0x50554B43, 0x20202020, 0x20202020,
      0x32524448, 0x30323046, 0x32303834, 0x20383430,
      0x20202020, 0x20202020, 0x20202020, 0x20202020,
      0x20202020, 0x2020204D, 0x20202020, 0x20202020,
      0x30302020, 0x20202020, 0x20202020, 0x20202020,
      0x20202020, 0x20202020, 0x20202020, 0x20202020};
/* Records in these hdrs are all 80 bytes long. Double eof
  separates taprecs and taprc2, and double eof follows taprc2.*/

	int taprc2[40]=
     {0x31464F45, 0x32385845, 0x202E3030, 0x20202020,
      0x20202020, 0x38584520, 0x30303032, 0x30313030,
      0x30313030, 0x30313030, 0x35392030, 0x20333731,
      0x37313539, 0x30302033, 0x32303030, 0x56434544,
      0x4142534D, 0x50554B43, 0x20202020, 0x20202020,
      0x32464F45, 0x30323046, 0x32303834, 0x20383430,
      0x20202020, 0x20202020, 0x20202020, 0x20202020,
      0x20202020, 0x2020204D, 0x20202020, 0x20202020,
      0x30302020, 0x20202020, 0x20202020, 0x20202020,
      0x20202020, 0x20202020, 0x20202020, 0x20202020};
       int tapdat[512]=
     {0x04000100,0x00010001,0x00000001,0x00000000,
      0,0,0,0,
      0x00010101,0xCFEDC41C,0x00000800,0x00000000,
      0x38584507,0x2E303032,0x00000000,0x00000000,
      0,0,0,0,0,0,0,0,0,0,0,
      0,0,0,0,0,0,0,0,0,0,0,
      0,0,0,0,0,0,0,0,0,0,0,
      0,0,0,0,0,0,0,0,0,0,0,
      0x00000000,0x00000000,0x00000000,0xDFE30000,
      0x000100D0,0x00000000,0x00000000,0x00000000,
      0x00070101,0x58450001,0x30303238,0x0200442E,
      0x43414200,0x45522F4B,0x4C422F57,0x3D4B434F,
      0x38343032,0x4E47492F,0x42414C3D,0x542F4C45,
      0x434E5552,0x4F52472F,0x303D5055,0x38584520,
      0x2E303032,0x204B4E4A,0x35424B4D,0x453A3030,
      0x30323858,0x04000C30,0x45564500,0x52414852,
      0x20202054,0x05000420,0x03000100,0x06000800,
      0x95782000,0x99482E3A,0x07000200,0x04040000,
      0x54000800,0x06322E36,0x5F000900,0x3A574F52,
      0x0A00043A,0x00000500,0x0B000C0A,0x4F525F00,
      0x4B4D2457,0x30303542,0x0C00043A,0x2E365600,
      0x0D000432,0x00080000,0x0E000200,0x02000000,
      0x9B000F00,0x00000002,0x00000000,0x00000000,
      0x000300F0,0x0,0,0,
      0x00160101,0x455B002A,0x48524556,0x5D545241,
      0x32385845,0x4A2E3030,0x313B4B4E,0x002B0002,
      0x00060201,0x1782002C,0x00010009,0x002E0004,
      0x00000003,0x002F0004,0x00030001,0x00340020,
      0x001D0202,0x00030000,0x00010000,0x00000020,
      0x0,0,0,0,
      0x002D0006,0x00050A35,0x00020000,0xFA000030,
      0x00310002,0x00010000,0x04000032,0x00003300,
      0x02000000,0xFF004B00,0x5000017F,0x00010000,
      0x04000057,0x04004F00,0x02000000,0x01003500,
      0x36000800,0xE557A000,0x9924322D,0x37000800,
      0x08700000,0x9924322E,0x38000800,0x00000000,
      0x00000000,0x39000800,0,0,
      0x47000400,0x03000100,0x48000200,0x02BA8800,
      0xFF004A00,0x0000007F,0,0,
      0x00040200,0x00000000,0x1,0,
      0x6854001D,0x69207369,0x75722073,0x73696262,
      0x6F662068,0x78452072,0x38202E61,0x00303032,
      0x0000FFFF,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
     0x00000300,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
       0,0,0,0,
     };

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, 0x10, 0, 0x96, 0},
	read_capacity_cmd [10] = {37, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	mode_select_cmd [6] = {21 , 0x10, 0, 0, 12, 0},
	read_cmd [6] = {8, 0, 0, 0, 0, 0},
	write_cmd [6] = {10, 0, 0, 0, 80, 0},
	bwrite_cmd [6] = {10, 0, 0, 8, 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},
	zweof_command[6] = {16, 0, 0,0,0,0},
	inquiry_data[INQUIRY_DATA_LENGTH],
        mode_sel_data[28] = {0x00, 0x0, 0x0, 0x08, 0x14, 0x00, 0x00,
	0x00, 00, 00, 00, 00, 0x10, 0x0d, 0x07, 00, 0x80, 0x80, 00, 00,
	0x40, 00, 0x18, 00, 00, 00, 00, 00},
	gk_device[] = {"TARGET_TAPE"};
/* set density 20 = Exabyte 8200 density */
/* 14 hex in next line is magic density */

main ()
{

/* Assign a channel to GKA0 */

	gk_device_desc[0] = strlen (gk_device);
	gk_device_desc[1] = &gk_device[0];
	status = sys$assign (&gk_device_desc[0], &gk_chan, 0, 0);
	if (!(status & 1)) {
		printf ("Unable to assign channel to %s", &gk_device[0]);
		sys$exit (status);
	}

	send_command ("Inquiry", inquiry_cmd, 6, 36);
/*	send_command ("Test unit ready", test_unit_ready_cmd, 6, 0);*/
/*	send_command ("Request sense", request_sense_cmd, 6, 18);*/
/*	send_command ("Start unit", start_unit_cmd, 6, 0);*/
	send_command ("Mode sense", mode_sense_cmd, 6, 150);
	send_command ("Request sense", request_sense_cmd, 6, 18);
/*	send_command ("Read capacity", read_capacity_cmd, 10, 8);*/
	send_command ("rewind", rewind_command, 6, 0);
	emit_command ("set mode", mode_select_cmd, 6, mode_sel_data, 12);
	send_command ("Request sense", request_sense_cmd, 6, 18);
	emit_command ("vol1", write_cmd, 6, &taprecs[0], 80);
	emit_command ("hdr1", write_cmd, 6, &taprecs[20], 80);
	emit_command ("hdr2", write_cmd, 6, &taprecs[40], 80);
	put_command ("zweof",  zweof_command, 6, 0);
	put_command ("weof",  weof_command, 6, 0);
/* put some data to skip*/
	emit_command ("data", bwrite_cmd, 6, tapdat, 2048);
	put_command ("zweof",  zweof_command, 6, 0);
	put_command ("weof",  weof_command, 6, 0);
	emit_command ("eof1", write_cmd, 6, &taprc2[0], 80);
	emit_command ("eof2", write_cmd, 6, &taprc2[20], 80);
	put_command ("zweof",  zweof_command, 6, 0);
	put_command ("weof",  weof_command, 6, 0);
	put_command ("weof",  weof_command, 6, 0);
}

send_command (cmd_name, cmd, cmd_len, data_len)
char *cmd_name, *cmd;
int cmd_len, data_len;
{
	char buf [512];
	char sensedata[512];

/*	printf ("------+++++----------------------------------\n\n");
        printf(" send_cmd\n");*/
/* 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_DISCONNECT + FLAGS_SYNCH;
	gk_desc[COMMAND_ADDRESS] = cmd;
	gk_desc[COMMAND_LENGTH] = cmd_len;
	gk_desc[DATA_ADDRESS] = buf;
	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)) sys$exit (status);
	if (!(gk_iosb[0] & 1)) sys$exit (gk_iosb[0] & 0xffff);
	scsi_status = (gk_iosb[1] >> 24) & SCSI_STATUS_MASK;
	if (scsi_status) {
		printf ("Bad SCSI status returned: %02.2x\n", scsi_status);
/*		for (i=0; i < 25; i++){
		if (i % 10 == 0) printf("\n ");
		printf ("%02x ",sensedata[i] & 0xff);
		} */
/*		sys$exit (1);*/
	}

/* The command succeeded. Display the SCSI command and 
   data returned from the target */

/*	printf ("----------------------------------------\n\n");
	printf ("%s command", cmd_name);
	for (i=0; i<cmd_len; i++) {
		if (i % 10 == 0) printf ("\n  ");
		printf ("%02x ", cmd[i] & 0xff);
	}
	printf ("\n\n");
*/
	transfer_length = gk_iosb[0] >> 16;
/*	printf ("Data returned");
	if (transfer_length == 0) {
		printf ("\n  None.");
	} else {
		for (i=0; i<transfer_length; i++) {
			if (i % 10 == 0) printf ("\n  ");
			printf ("%02x ", buf[i] & 0xff);
		}
	}
	printf ("\n\n");
*/
}

emit_command (cmd_name, cmd, cmd_len, indata, data_len)
char *cmd_name, *cmd, *indata;
int cmd_len, data_len;
{
	int kkk,kkkk;
	char *pk;
	char buf [2512];
	char sensedata[512];

	for (i=0;i<511;i++)sensedata[i] = 0;
	kkkk = 0;
	pk = indata;
	for (kkk = data_len; kkk > 0; kkk--){
	  buf[kkkk] = *pk;
	  kkkk++;
	  pk++;
	}
/*	printf ("------+++++----------------------------------\n\n");
        printf(" Data length requested %d\n",kkkk);*/
/* 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;
	gk_desc[COMMAND_ADDRESS] = cmd;
	gk_desc[COMMAND_LENGTH] = cmd_len;
	gk_desc[DATA_ADDRESS] = buf;
	gk_desc[DATA_LENGTH] = data_len;
	gk_desc[PAD_LENGTH] = 0;
	gk_desc[PHASE_TIMEOUT] = 100;
	gk_desc[DISCONNECT_TIMEOUT] = 100;
	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)) sys$exit (status);
	if (!(gk_iosb[0] & 1)) sys$exit (gk_iosb[0] & 0xffff);
	scsi_status = (gk_iosb[1] >> 24) & SCSI_STATUS_MASK;
	if (scsi_status) {
		printf ("Bad SCSI status returned: %02.2x\n", scsi_status);
/*		for (i=0; i < 25; i++){
		if (i % 10 == 0) printf("\n ");
		printf ("%02x ",sensedata[i] & 0xff);
		}*/
/*		sys$exit (1);*/
	}

/* The command succeeded. Display the SCSI command and 
   data returned from the target */

/*	printf ("----------------------------------------\n\n");
	printf ("%s command", cmd_name);
	for (i=0; i<cmd_len; i++) {
		if (i % 10 == 0) printf ("\n  ");
		printf ("%02x ", cmd[i] & 0xff);
	}
	printf ("\n\n");
*/
	transfer_length = gk_iosb[0] >> 16;
/*	printf ("Data returned");
	if (transfer_length == 0) {
		printf ("\n  None.");
	} else {
		if (transfer_length > 19) transfer_length=19;
		for (i=0; i<transfer_length; i++) {
			if (i % 10 == 0) printf ("\n  ");
			printf ("%02x ", buf[i] & 0xff);
		}
	}
	printf ("\n\n");
*/

}

put_command (cmd_name, cmd, cmd_len, data_len)
char *cmd_name, *cmd;
int cmd_len, data_len;
{
	char buf [512];
/*	char sensedata[512];*/
	int kkkk;
	kkkk = data_len;
/*	printf ("------+++++----------------------------------\n\n");
        printf(" put cmd Data length requested %d\n",kkkk);*/
/* 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_WRITE + FLAGS_DISCONNECT + FLAGS_SYNCH;
	gk_desc[COMMAND_ADDRESS] = cmd;
	gk_desc[COMMAND_LENGTH] = cmd_len;
	gk_desc[DATA_ADDRESS] = buf;
	gk_desc[DATA_LENGTH] = data_len;
	gk_desc[PAD_LENGTH] = 0;
	gk_desc[PHASE_TIMEOUT] = 120;
	gk_desc[DISCONNECT_TIMEOUT] = 120;
	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)) printf(" qio status %d\n",status);
	if (!(gk_iosb[0] & 1))printf(" iosb %d\n",gk_iosb[0]);
/*	if (!(status & 1)) sys$exit (status);
	if (!(gk_iosb[0] & 1)) sys$exit (gk_iosb[0] & 0xffff);*/
	scsi_status = (gk_iosb[1] >> 24) & SCSI_STATUS_MASK;
	if (scsi_status) {
		printf ("Bad SCSI status returned: %02.2x\n", scsi_status);
/*		for (i=0; i < 25; i++){
		if (i % 10 == 0) printf("\n ");
		printf ("%02x ",sensedata[i] & 0xff);
		} */
/*		sys$exit (1);*/
	}

/* The command succeeded. Display the SCSI command and 
   data returned from the target */

/*	printf ("----------------------------------------\n\n");
	printf ("%s command", cmd_name);
	for (i=0; i<cmd_len; i++) {
		if (i % 10 == 0) printf ("\n  ");
		printf ("%02x ", cmd[i] & 0xff);
	}
	printf ("\n\n");
*/
	transfer_length = gk_iosb[0] >> 16;
/*	printf ("Data returned");
	if (transfer_length == 0) {
		printf ("\n  None.");
	} else {
		for (i=0; i<transfer_length; i++) {
			if (i % 10 == 0) printf ("\n  ");
			printf ("%02x ", buf[i] & 0xff);
		}
	}
	printf ("\n\n");
*/
}
