1 /* SCSI.C - VMS-specific SCSI routines.
3 ** TECSys Development, Inc., April 1998
5 ** This module began life as a program called CDWRITE20, a CD-R control
6 ** program for VMS. No real functionality from the original CDWRITE20
7 ** is present in this module, but in the interest of making certain that
8 ** proper credit is given where it may be due, the copyrights and inclusions
9 ** from the CDWRITE20 program are included below.
11 ** The portions of coding in this module ascribable to TECSys Development
12 ** are hereby also released under the terms and conditions of version 2
13 ** of the GNU General Public License as described below....
16 /* The remainder of these credits are included directly from the CDWRITE20
19 /* Copyright 1994 Yggdrasil Computing, Inc. */
20 /* Written by Adam J. Richter (adam@yggdrasil.com) */
22 /* Rewritten February 1997 by Eberhard Heuser-Hofmann*/
23 /* using the OpenVMS generic scsi-interface */
24 /* see the README-file, how to setup your machine */
27 ** Modified March 1997 by John Vottero to use overlapped, async I/O
28 ** and lots of buffers to help prevent buffer underruns.
29 ** Also improved error reporting.
32 /* This file may be copied under the terms and conditions of version 2
33 of the GNU General Public License, as published by the Free
34 Software Foundation (Cambridge, Massachusetts).
36 This program is distributed in the hope that it will be useful,
37 but WITHOUT ANY WARRANTY; without even the implied warranty of
38 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 GNU General Public License for more details.
41 You should have received a copy of the GNU General Public License
42 along with this program; if not, write to the Free Software
43 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
45 /* The second notice comes from sys$examples:gktest.c (OpenVMS 7.0)*/
48 ** COPYRIGHT (c) 1993 BY
49 ** DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS.
50 ** ALL RIGHTS RESERVED.
52 ** THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED
53 ** ONLY IN ACCORDANCE OF THE TERMS OF SUCH LICENSE AND WITH THE
54 ** INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER
55 ** COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
56 ** OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY
59 ** THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE
60 ** AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT
63 ** DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS
64 ** SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.
68 Define the Generic SCSI Command Descriptor.
71 typedef struct scsi$desc
73 unsigned int SCSI$L_OPCODE; /* SCSI Operation Code */
74 unsigned int SCSI$L_FLAGS; /* SCSI Flags Bit Map */
75 unsigned char *SCSI$A_CMD_ADDR; /* ->SCSI Command Buffer */
76 unsigned int SCSI$L_CMD_LEN; /* SCSI Command Length (bytes) */
77 unsigned char *SCSI$A_DATA_ADDR; /* ->SCSI Data Buffer */
78 unsigned int SCSI$L_DATA_LEN; /* SCSI Data Length (bytes) */
79 unsigned int SCSI$L_PAD_LEN; /* SCSI Pad Length (bytes) */
80 unsigned int SCSI$L_PH_CH_TMOUT; /* SCSI Phase Change Timeout (seconds) */
81 unsigned int SCSI$L_DISCON_TMOUT; /* SCSI Disconnect Timeout (seconds) */
82 unsigned int SCSI$L_RES_1; /* Reserved */
83 unsigned int SCSI$L_RES_2; /* Reserved */
84 unsigned int SCSI$L_RES_3; /* Reserved */
85 unsigned int SCSI$L_RES_4; /* Reserved */
86 unsigned int SCSI$L_RES_5; /* Reserved */
87 unsigned int SCSI$L_RES_6; /* Reserved */
93 Define the SCSI Input/Output Status Block.
96 typedef struct scsi$iosb
98 unsigned short SCSI$W_VMS_STAT; /* VMS Status Code */
99 unsigned long SCSI$L_IOSB_TFR_CNT; /* Actual Byte Count Transferred */
100 unsigned char SCSI$B_IOSB_FILL_1; /* Unused */
101 unsigned char SCSI$B_IOSB_STS; /* SCSI Device Status */
107 Define the VMS symbolic representation for a successful SCSI command.
110 #define SCSI$K_GOOD 0
114 Define the SCSI Flag Field Constants.
117 #define SCSI$K_WRITE 0x0 /* Data Transfer Direction: Write */
118 #define SCSI$K_READ 0x1 /* Data Transfer Direction: Read */
119 #define SCSI$K_FL_ENAB_DIS 0x2 /* Enable Disconnect/Reconnect */
123 Define DESCR_CNT. It must be a power of two and large enough
124 for the maximum concurrent usage of descriptors.
130 #define MK_EFN 0 /* Event Flag Number */
131 #define FailureStatusP(Status) (~(Status) & 1)
134 static struct dsc$descriptor_s *descr(char *String)
136 static struct dsc$descriptor_s d_descrtbl[DESCR_CNT];
137 static unsigned short descridx = 0;
138 struct dsc$descriptor_s *d_ret = &d_descrtbl[descridx];
139 descridx = (descridx + 1) & (DESCR_CNT - 1);
140 d_ret->dsc$w_length = strlen((const char *) String);
141 d_ret->dsc$a_pointer = String;
142 d_ret->dsc$b_class = 0;
143 d_ret->dsc$b_dtype = 0;
149 static int SCSI_OpenDevice(char *DeviceName)
151 unsigned long d_dev[2], iosb[2], Status;
152 union prvdef setprivs, newprivs;
153 unsigned long ismnt = 0;
154 unsigned long dvcls = 0;
155 unsigned long dchr2 = 0;
161 unsigned long *returnP;
162 unsigned long ignored;
165 { 4, DVI$_MNT, 0 /*&ismnt*/, 0 },
166 { 4, DVI$_DEVCLASS, 0 /*&dvcls*/, 0 },
167 { 4, DVI$_DEVCHAR2, 0 /*&dchr2*/, 0 },
171 dvi_itmlst[0].returnP = &ismnt;
172 dvi_itmlst[1].returnP = &dvcls;
173 dvi_itmlst[2].returnP = &dchr2;
175 Status = sys$alloc(descr(DeviceName), 0, 0, 0, 0);
177 if (FailureStatusP(Status))
179 VMS_ExitCode = Status;
180 FatalError("cannot allocate device '%s' - %X\n", DeviceName, Status);
183 Status = sys$assign(descr(DeviceName), &DeviceFD, 0, 0);
184 if (FailureStatusP(Status))
186 VMS_ExitCode = Status;
187 FatalError("cannot open device '%s' - %X\n", DeviceName, Status);
190 Status = sys$getdviw(0, DeviceFD, 0, &dvi_itmlst, &iosb, 0, 0, 0);
191 if (FailureStatusP(Status))
193 VMS_ExitCode = Status;
194 FatalError("cannot $getdvi(1) on device '%s' - %X\n", DeviceName, Status);
197 if (FailureStatusP(Status = iosb[0]))
199 VMS_ExitCode = Status;
200 FatalError("cannot $getdvi(2) on device '%s' - %X\n", DeviceName, Status);
203 if (dvcls != DC$_TAPE)
205 VMS_ExitCode = SS$_IVDEVNAM;
206 FatalError("specified device is NOT a magtape: operation denied\n");
210 #define DEV$M_SCSI 0x1000000
213 if (~dchr2 & DEV$M_SCSI)
215 VMS_ExitCode = SS$_IVDEVNAM;
216 FatalError("specified magtape is NOT a SCSI device: operation denied\n");
218 #if USING_DEC_DRIVE | USING_LDRSET
221 #define DEV$M_LDR 0x100000
224 if (~dchr2 & DEV$M_LDR)
226 VMS_ExitCode = SS$_IVDEVNAM;
227 FatalError("specified SCSI magtape does not have a loader: operation denied\n");
232 VMS_ExitCode = SS$_DEVMOUNT;
233 FatalError("specified device is mounted: operation denied\n");
236 ots$move5(0, 0, 0, sizeof(newprivs), &newprivs);
237 newprivs.prv$v_diagnose = 1;
238 newprivs.prv$v_log_io = 1;
239 newprivs.prv$v_phy_io = 1;
240 Status = sys$setprv(1, &newprivs, 0, 0);
242 if (FailureStatusP(Status))
244 VMS_ExitCode = Status;
245 FatalError("error enabling privs (diagnose,log_io,phy_io): operation denied\n");
248 Status = sys$setprv(1, 0, 0, &setprivs);
249 if (FailureStatusP(Status))
251 VMS_ExitCode = Status;
252 FatalError("error retrieving current privs: operation denied\n");
255 if (!setprivs.prv$v_diagnose)
257 VMS_ExitCode = SS$_NODIAGNOSE;
258 FatalError("DIAGNOSE privilege is required: operation denied\n");
261 if (!setprivs.prv$v_phy_io && !setprivs.prv$v_log_io)
263 VMS_ExitCode = SS$_NOPHY_IO;
264 FatalError("PHY_IO or LOG_IO privilege is required: operation denied\n");
271 static void SCSI_CloseDevice(char *DeviceName, int DeviceFD)
273 unsigned long Status;
275 Status = sys$dassgn(DeviceFD);
276 if (FailureStatusP(Status))
277 FatalError("cannot close SCSI device '%s' - %X\n", DeviceName, Status);
281 static int SCSI_ExecuteCommand( int DeviceFD,
282 Direction_T Direction,
286 int DataBufferLength,
287 RequestSense_T *RequestSense)
291 unsigned long Status;
293 memset(RequestSense, 0, sizeof(RequestSense_T));
294 /* Issue the QIO to send the SCSI Command. */
295 ots$move5(0, 0, 0, sizeof(cmd_desc), &cmd_desc);
296 cmd_desc.SCSI$L_OPCODE = 1; /* Only defined SCSI opcode... a VMS thing */
297 cmd_desc.SCSI$A_CMD_ADDR = CDB;
298 cmd_desc.SCSI$L_CMD_LEN = CDB_Length;
299 cmd_desc.SCSI$A_DATA_ADDR = DataBuffer;
300 cmd_desc.SCSI$L_DATA_LEN = DataBufferLength;
301 cmd_desc.SCSI$L_PAD_LEN = 0;
302 cmd_desc.SCSI$L_PH_CH_TMOUT = 180; /* SCSI Phase Change Timeout (seconds) */
303 cmd_desc.SCSI$L_DISCON_TMOUT = 180; /* SCSI Disconnect Timeout (seconds) */
308 NOTE: Do NOT include flag SCSI$K_FL_ENAB_SYNC.
309 It does NOT work for this case.
312 cmd_desc.SCSI$L_FLAGS = SCSI$K_READ | SCSI$K_FL_ENAB_DIS;
316 cmd_desc.SCSI$L_FLAGS = SCSI$K_WRITE | SCSI$K_FL_ENAB_DIS;
320 /* Issue the SCSI Command. */
321 Status = sys$qiow(MK_EFN, DeviceFD, IO$_DIAGNOSE, &cmd_iosb, 0, 0,
322 &cmd_desc, sizeof(cmd_desc), 0, 0, 0, 0);
323 Result = SCSI$K_GOOD;
326 Status = cmd_iosb.SCSI$W_VMS_STAT;
329 Result = cmd_iosb.SCSI$B_IOSB_STS;
331 if (Result != SCSI$K_GOOD)
333 unsigned char RequestSenseCDB[6] =
334 { 0x03 /* REQUEST_SENSE */, 0, 0, 0, sizeof(RequestSense_T), 0 };
336 printf("SCSI command error: %d - requesting sense data\n", Result);
338 /* Execute Request Sense to determine the failure reason. */
339 ots$move5(0, 0, 0, sizeof(cmd_desc), &cmd_desc);
340 cmd_desc.SCSI$L_OPCODE = 1; /* Only defined SCSI opcode... a VMS thing */
341 cmd_desc.SCSI$L_FLAGS = SCSI$K_READ | SCSI$K_FL_ENAB_DIS;
342 cmd_desc.SCSI$A_CMD_ADDR = RequestSenseCDB;
343 cmd_desc.SCSI$L_CMD_LEN = 6;
344 cmd_desc.SCSI$A_DATA_ADDR = RequestSense;
345 cmd_desc.SCSI$L_DATA_LEN = sizeof(RequestSense_T);
346 cmd_desc.SCSI$L_PH_CH_TMOUT = 180;
347 cmd_desc.SCSI$L_DISCON_TMOUT = 180;
349 /* Issue the QIO to send the Request Sense Command. */
350 Status = sys$qiow(MK_EFN, DeviceFD, IO$_DIAGNOSE, &cmd_iosb, 0, 0,
351 &cmd_desc, sizeof(cmd_desc), 0, 0, 0, 0);
352 if (FailureStatusP(Status))
354 printf("?Error returned from REQUEST_SENSE(1): %%X%08lX\n", Status);
358 /* Check the VMS Status from QIO. */
359 Status = cmd_iosb.SCSI$W_VMS_STAT;
360 if (FailureStatusP(Status))
362 printf("?Error returned from REQUEST_SENSE(2): %%X%08lX\n", Status);
370 static void VMS_DefineStatusSymbols(void)
372 char SymbolName[32], SymbolValue[32];
373 int StorageElementNumber;
375 if (DataTransferElementFull)
377 /* Define MTX_DTE Symbol (environment variable) as 'FULL:n'. */
378 sprintf(SymbolValue, "FULL:%d",
379 DataTransferElementSourceStorageElementNumber);
380 lib$set_symbol(descr("MTX_DTE"), descr(SymbolValue), &2);
384 /* Define MTX_DTE Symbol (environment variable) as 'EMPTY'. */
385 lib$set_symbol(descr("MTX_DTE"), descr("EMPTY"), &2);
388 /* Define MTX_MSZ Symbol (environment variable) "Magazine SiZe" as 'n'. */
389 sprintf(SymbolValue, "%d", StorageElementCount);
390 lib$set_symbol(descr("MTX_MSZ"), descr(SymbolValue), &2);
391 for (StorageElementNumber = 1;
392 StorageElementNumber <= StorageElementCount;
393 StorageElementNumber++)
395 sprintf(SymbolName, "MTX_STE%02d", StorageElementNumber);
397 (StorageElementFull[StorageElementNumber] ? "FULL" : "EMPTY"));
398 lib$set_symbol(descr(SymbolName), descr(SymbolValue), &2);