/* SCSI.C - VMS-specific SCSI routines. ** ** TECSys Development, Inc., April 1998 ** ** This module began life as a program called CDWRITE20, a CD-R control ** program for VMS. No real functionality from the original CDWRITE20 ** is present in this module, but in the interest of making certain that ** proper credit is given where it may be due, the copyrights and inclusions ** from the CDWRITE20 program are included below. ** ** The portions of coding in this module ascribable to TECSys Development ** are hereby also released under the terms and conditions of version 2 ** of the GNU General Public License as described below.... */ /* The remainder of these credits are included directly from the CDWRITE20 ** sources. */ /* Copyright 1994 Yggdrasil Computing, Inc. */ /* Written by Adam J. Richter (adam@yggdrasil.com) */ /* Rewritten February 1997 by Eberhard Heuser-Hofmann*/ /* using the OpenVMS generic scsi-interface */ /* see the README-file, how to setup your machine */ /* ** Modified March 1997 by John Vottero to use overlapped, async I/O ** and lots of buffers to help prevent buffer underruns. ** Also improved error reporting. */ /* This file may be copied under the terms and conditions of version 2 of the GNU General Public License, as published by the Free Software Foundation (Cambridge, Massachusetts). This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* The second notice comes from sys$examples:gktest.c (OpenVMS 7.0)*/ /* ** COPYRIGHT (c) 1993 BY ** DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS. ** ALL RIGHTS RESERVED. ** ** THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED ** ONLY IN ACCORDANCE OF THE TERMS OF SUCH LICENSE AND WITH THE ** INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER ** COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY ** OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY ** TRANSFERRED. ** ** THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE ** AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT ** CORPORATION. ** ** DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS ** SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL. */ /* Define the Generic SCSI Command Descriptor. */ typedef struct scsi$desc { unsigned int SCSI$L_OPCODE; /* SCSI Operation Code */ unsigned int SCSI$L_FLAGS; /* SCSI Flags Bit Map */ unsigned char *SCSI$A_CMD_ADDR; /* ->SCSI Command Buffer */ unsigned int SCSI$L_CMD_LEN; /* SCSI Command Length (bytes) */ unsigned char *SCSI$A_DATA_ADDR; /* ->SCSI Data Buffer */ unsigned int SCSI$L_DATA_LEN; /* SCSI Data Length (bytes) */ unsigned int SCSI$L_PAD_LEN; /* SCSI Pad Length (bytes) */ unsigned int SCSI$L_PH_CH_TMOUT; /* SCSI Phase Change Timeout (seconds) */ unsigned int SCSI$L_DISCON_TMOUT; /* SCSI Disconnect Timeout (seconds) */ unsigned int SCSI$L_RES_1; /* Reserved */ unsigned int SCSI$L_RES_2; /* Reserved */ unsigned int SCSI$L_RES_3; /* Reserved */ unsigned int SCSI$L_RES_4; /* Reserved */ unsigned int SCSI$L_RES_5; /* Reserved */ unsigned int SCSI$L_RES_6; /* Reserved */ } SCSI$DESC; /* Define the SCSI Input/Output Status Block. */ typedef struct scsi$iosb { unsigned short SCSI$W_VMS_STAT; /* VMS Status Code */ unsigned long SCSI$L_IOSB_TFR_CNT; /* Actual Byte Count Transferred */ unsigned char SCSI$B_IOSB_FILL_1; /* Unused */ unsigned char SCSI$B_IOSB_STS; /* SCSI Device Status */ } SCSI$IOSB; /* Define the VMS symbolic representation for a successful SCSI command. */ #define SCSI$K_GOOD 0 /* Define the SCSI Flag Field Constants. */ #define SCSI$K_WRITE 0x0 /* Data Transfer Direction: Write */ #define SCSI$K_READ 0x1 /* Data Transfer Direction: Read */ #define SCSI$K_FL_ENAB_DIS 0x2 /* Enable Disconnect/Reconnect */ /* Define DESCR_CNT. It must be a power of two and large enough for the maximum concurrent usage of descriptors. */ #define DESCR_CNT 16 #define MK_EFN 0 /* Event Flag Number */ #define FailureStatusP(Status) (~(Status) & 1) static struct dsc$descriptor_s *descr(char *String) { static struct dsc$descriptor_s d_descrtbl[DESCR_CNT]; static unsigned short descridx = 0; struct dsc$descriptor_s *d_ret = &d_descrtbl[descridx]; descridx = (descridx + 1) & (DESCR_CNT - 1); d_ret->dsc$w_length = strlen((const char *) String); d_ret->dsc$a_pointer = String; d_ret->dsc$b_class = 0; d_ret->dsc$b_dtype = 0; return d_ret; } static int SCSI_OpenDevice(char *DeviceName) { unsigned long d_dev[2], iosb[2], Status; union prvdef setprivs, newprivs; unsigned long ismnt = 0; unsigned long dvcls = 0; unsigned long dchr2 = 0; int DeviceFD = 0; struct itmlst_3 { unsigned short ilen; unsigned short code; unsigned long *returnP; unsigned long ignored; } dvi_itmlst[] = { { 4, DVI$_MNT, 0 /*&ismnt*/, 0 }, { 4, DVI$_DEVCLASS, 0 /*&dvcls*/, 0 }, { 4, DVI$_DEVCHAR2, 0 /*&dchr2*/, 0 }, { 0, 0, 0, 0 } }; dvi_itmlst[0].returnP = &ismnt; dvi_itmlst[1].returnP = &dvcls; dvi_itmlst[2].returnP = &dchr2; Status = sys$alloc(descr(DeviceName), 0, 0, 0, 0); if (FailureStatusP(Status)) { VMS_ExitCode = Status; FatalError("cannot allocate device '%s' - %X\n", DeviceName, Status); } Status = sys$assign(descr(DeviceName), &DeviceFD, 0, 0); if (FailureStatusP(Status)) { VMS_ExitCode = Status; FatalError("cannot open device '%s' - %X\n", DeviceName, Status); } Status = sys$getdviw(0, DeviceFD, 0, &dvi_itmlst, &iosb, 0, 0, 0); if (FailureStatusP(Status)) { VMS_ExitCode = Status; FatalError("cannot $getdvi(1) on device '%s' - %X\n", DeviceName, Status); } if (FailureStatusP(Status = iosb[0])) { VMS_ExitCode = Status; FatalError("cannot $getdvi(2) on device '%s' - %X\n", DeviceName, Status); } if (dvcls != DC$_TAPE) { VMS_ExitCode = SS$_IVDEVNAM; FatalError("specified device is NOT a magtape: operation denied\n"); } #ifndef __DECC #ifndef DEV$M_SCSI #define DEV$M_SCSI 0x1000000 #endif #endif if (~dchr2 & DEV$M_SCSI) { VMS_ExitCode = SS$_IVDEVNAM; FatalError("specified magtape is NOT a SCSI device: operation denied\n"); } #if USING_DEC_DRIVE | USING_LDRSET #ifndef __DECC #ifndef DEV$M_LDR #define DEV$M_LDR 0x100000 #endif #endif if (~dchr2 & DEV$M_LDR) { VMS_ExitCode = SS$_IVDEVNAM; FatalError("specified SCSI magtape does not have a loader: operation denied\n"); } #endif if (ismnt) { VMS_ExitCode = SS$_DEVMOUNT; FatalError("specified device is mounted: operation denied\n"); } ots$move5(0, 0, 0, sizeof(newprivs), &newprivs); newprivs.prv$v_diagnose = 1; newprivs.prv$v_log_io = 1; newprivs.prv$v_phy_io = 1; Status = sys$setprv(1, &newprivs, 0, 0); if (FailureStatusP(Status)) { VMS_ExitCode = Status; FatalError("error enabling privs (diagnose,log_io,phy_io): operation denied\n"); } Status = sys$setprv(1, 0, 0, &setprivs); if (FailureStatusP(Status)) { VMS_ExitCode = Status; FatalError("error retrieving current privs: operation denied\n"); } if (!setprivs.prv$v_diagnose) { VMS_ExitCode = SS$_NODIAGNOSE; FatalError("DIAGNOSE privilege is required: operation denied\n"); } if (!setprivs.prv$v_phy_io && !setprivs.prv$v_log_io) { VMS_ExitCode = SS$_NOPHY_IO; FatalError("PHY_IO or LOG_IO privilege is required: operation denied\n"); } return DeviceFD; } static void SCSI_CloseDevice(char *DeviceName, int DeviceFD) { unsigned long Status; Status = sys$dassgn(DeviceFD); if (FailureStatusP(Status)) FatalError("cannot close SCSI device '%s' - %X\n", DeviceName, Status); } static int SCSI_ExecuteCommand( int DeviceFD, Direction_T Direction, CDB_T *CDB, int CDB_Length, void *DataBuffer, int DataBufferLength, RequestSense_T *RequestSense) { SCSI$DESC cmd_desc; SCSI$IOSB cmd_iosb; unsigned long Status; int Result; memset(RequestSense, 0, sizeof(RequestSense_T)); /* Issue the QIO to send the SCSI Command. */ ots$move5(0, 0, 0, sizeof(cmd_desc), &cmd_desc); cmd_desc.SCSI$L_OPCODE = 1; /* Only defined SCSI opcode... a VMS thing */ cmd_desc.SCSI$A_CMD_ADDR = CDB; cmd_desc.SCSI$L_CMD_LEN = CDB_Length; cmd_desc.SCSI$A_DATA_ADDR = DataBuffer; cmd_desc.SCSI$L_DATA_LEN = DataBufferLength; cmd_desc.SCSI$L_PAD_LEN = 0; cmd_desc.SCSI$L_PH_CH_TMOUT = 180; /* SCSI Phase Change Timeout (seconds) */ cmd_desc.SCSI$L_DISCON_TMOUT = 180; /* SCSI Disconnect Timeout (seconds) */ switch (Direction) { /* NOTE: Do NOT include flag SCSI$K_FL_ENAB_SYNC. It does NOT work for this case. */ case Input: cmd_desc.SCSI$L_FLAGS = SCSI$K_READ | SCSI$K_FL_ENAB_DIS; break; case Output: cmd_desc.SCSI$L_FLAGS = SCSI$K_WRITE | SCSI$K_FL_ENAB_DIS; break; } /* Issue the SCSI Command. */ Status = sys$qiow(MK_EFN, DeviceFD, IO$_DIAGNOSE, &cmd_iosb, 0, 0, &cmd_desc, sizeof(cmd_desc), 0, 0, 0, 0); Result = SCSI$K_GOOD; if (Status & 1) Status = cmd_iosb.SCSI$W_VMS_STAT; if (Status & 1) Result = cmd_iosb.SCSI$B_IOSB_STS; if (Result != SCSI$K_GOOD) { unsigned char RequestSenseCDB[6] = { 0x03 /* REQUEST_SENSE */, 0, 0, 0, sizeof(RequestSense_T), 0 }; printf("SCSI command error: %d - requesting sense data\n", Result); /* Execute Request Sense to determine the failure reason. */ ots$move5(0, 0, 0, sizeof(cmd_desc), &cmd_desc); cmd_desc.SCSI$L_OPCODE = 1; /* Only defined SCSI opcode... a VMS thing */ cmd_desc.SCSI$L_FLAGS = SCSI$K_READ | SCSI$K_FL_ENAB_DIS; cmd_desc.SCSI$A_CMD_ADDR = RequestSenseCDB; cmd_desc.SCSI$L_CMD_LEN = 6; cmd_desc.SCSI$A_DATA_ADDR = RequestSense; cmd_desc.SCSI$L_DATA_LEN = sizeof(RequestSense_T); cmd_desc.SCSI$L_PH_CH_TMOUT = 180; cmd_desc.SCSI$L_DISCON_TMOUT = 180; /* Issue the QIO to send the Request Sense Command. */ Status = sys$qiow(MK_EFN, DeviceFD, IO$_DIAGNOSE, &cmd_iosb, 0, 0, &cmd_desc, sizeof(cmd_desc), 0, 0, 0, 0); if (FailureStatusP(Status)) { printf("?Error returned from REQUEST_SENSE(1): %%X%08lX\n", Status); sys$exit(Status); } /* Check the VMS Status from QIO. */ Status = cmd_iosb.SCSI$W_VMS_STAT; if (FailureStatusP(Status)) { printf("?Error returned from REQUEST_SENSE(2): %%X%08lX\n", Status); sys$exit(Status); } } return Result; } static void VMS_DefineStatusSymbols(void) { char SymbolName[32], SymbolValue[32]; int StorageElementNumber; if (DataTransferElementFull) { /* Define MTX_DTE Symbol (environment variable) as 'FULL:n'. */ sprintf(SymbolValue, "FULL:%d", DataTransferElementSourceStorageElementNumber); lib$set_symbol(descr("MTX_DTE"), descr(SymbolValue), &2); } else { /* Define MTX_DTE Symbol (environment variable) as 'EMPTY'. */ lib$set_symbol(descr("MTX_DTE"), descr("EMPTY"), &2); } /* Define MTX_MSZ Symbol (environment variable) "Magazine SiZe" as 'n'. */ sprintf(SymbolValue, "%d", StorageElementCount); lib$set_symbol(descr("MTX_MSZ"), descr(SymbolValue), &2); for (StorageElementNumber = 1; StorageElementNumber <= StorageElementCount; StorageElementNumber++) { sprintf(SymbolName, "MTX_STE%02d", StorageElementNumber); strcpy(SymbolValue, (StorageElementFull[StorageElementNumber] ? "FULL" : "EMPTY")); lib$set_symbol(descr(SymbolName), descr(SymbolValue), &2); } }