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;
148 static int SCSI_OpenDevice(char *DeviceName)
150 unsigned long d_dev[2], iosb[2], Status;
151 union prvdef setprivs, newprivs;
152 unsigned long ismnt = 0;
153 unsigned long dvcls = 0;
154 unsigned long dchr2 = 0;
159 unsigned long *returnP;
160 unsigned long ignored;
161 } dvi_itmlst[] = { { 4, DVI$_MNT, 0 /*&ismnt*/, 0 },
162 { 4, DVI$_DEVCLASS, 0 /*&dvcls*/, 0 },
163 { 4, DVI$_DEVCHAR2, 0 /*&dchr2*/, 0 },
165 dvi_itmlst[0].returnP = &ismnt;
166 dvi_itmlst[1].returnP = &dvcls;
167 dvi_itmlst[2].returnP = &dchr2;
168 Status = sys$alloc(descr(DeviceName), 0, 0, 0, 0);
169 if (FailureStatusP(Status))
171 VMS_ExitCode = Status;
172 FatalError("cannot allocate device '%s' - %X\n", DeviceName, Status);
174 Status = sys$assign(descr(DeviceName), &DeviceFD, 0, 0);
175 if (FailureStatusP(Status))
177 VMS_ExitCode = Status;
178 FatalError("cannot open device '%s' - %X\n", DeviceName, Status);
180 Status = sys$getdviw(0, DeviceFD, 0, &dvi_itmlst, &iosb, 0, 0, 0);
181 if (FailureStatusP(Status))
183 VMS_ExitCode = Status;
184 FatalError("cannot $getdvi(1) on device '%s' - %X\n", DeviceName, Status);
186 if (FailureStatusP(Status = iosb[0]))
188 VMS_ExitCode = Status;
189 FatalError("cannot $getdvi(2) on device '%s' - %X\n", DeviceName, Status);
191 if (dvcls != DC$_TAPE)
193 VMS_ExitCode = SS$_IVDEVNAM;
194 FatalError("specified device is NOT a magtape: operation denied\n");
198 #define DEV$M_SCSI 0x1000000
201 if (~dchr2 & DEV$M_SCSI)
203 VMS_ExitCode = SS$_IVDEVNAM;
204 FatalError("specified magtape is NOT a SCSI device: operation denied\n");
206 #if USING_DEC_DRIVE | USING_LDRSET
209 #define DEV$M_LDR 0x100000
212 if (~dchr2 & DEV$M_LDR)
214 VMS_ExitCode = SS$_IVDEVNAM;
216 "specified SCSI magtape does not have a loader: operation denied\n");
221 VMS_ExitCode = SS$_DEVMOUNT;
222 FatalError("specified device is mounted: operation denied\n");
224 ots$move5(0, 0, 0, sizeof(newprivs), &newprivs);
225 newprivs.prv$v_diagnose = 1;
226 newprivs.prv$v_log_io = 1;
227 newprivs.prv$v_phy_io = 1;
228 Status = sys$setprv(1, &newprivs, 0, 0);
229 if (FailureStatusP(Status))
231 VMS_ExitCode = Status;
233 "error enabling privs (diagnose,log_io,phy_io): operation denied\n");
235 Status = sys$setprv(1, 0, 0, &setprivs);
236 if (FailureStatusP(Status))
238 VMS_ExitCode = Status;
239 FatalError("error retrieving current privs: operation denied\n");
241 if (!setprivs.prv$v_diagnose)
243 VMS_ExitCode = SS$_NODIAGNOSE;
244 FatalError("DIAGNOSE privilege is required: operation denied\n");
246 if (!setprivs.prv$v_phy_io && !setprivs.prv$v_log_io)
248 VMS_ExitCode = SS$_NOPHY_IO;
249 FatalError("PHY_IO or LOG_IO privilege is required: operation denied\n");
255 static void SCSI_CloseDevice(char *DeviceName,
258 unsigned long Status;
259 Status = sys$dassgn(DeviceFD);
260 if (FailureStatusP(Status))
261 FatalError("cannot close SCSI device '%s' - %X\n", DeviceName, Status);
265 static int SCSI_ExecuteCommand(int DeviceFD,
266 Direction_T Direction,
270 int DataBufferLength,
271 RequestSense_T *RequestSense)
275 unsigned long Status;
277 memset(RequestSense, 0, sizeof(RequestSense_T));
278 /* Issue the QIO to send the SCSI Command. */
279 ots$move5(0, 0, 0, sizeof(cmd_desc), &cmd_desc);
280 cmd_desc.SCSI$L_OPCODE = 1; /* Only defined SCSI opcode... a VMS thing */
281 cmd_desc.SCSI$A_CMD_ADDR = CDB;
282 cmd_desc.SCSI$L_CMD_LEN = CDB_Length;
283 cmd_desc.SCSI$A_DATA_ADDR = DataBuffer;
284 cmd_desc.SCSI$L_DATA_LEN = DataBufferLength;
285 cmd_desc.SCSI$L_PAD_LEN = 0;
286 cmd_desc.SCSI$L_PH_CH_TMOUT = 180; /* SCSI Phase Change Timeout (seconds) */
287 cmd_desc.SCSI$L_DISCON_TMOUT = 180; /* SCSI Disconnect Timeout (seconds) */
291 NOTE: Do NOT include flag SCSI$K_FL_ENAB_SYNC.
292 It does NOT work for this case.
295 cmd_desc.SCSI$L_FLAGS = SCSI$K_READ | SCSI$K_FL_ENAB_DIS;
298 cmd_desc.SCSI$L_FLAGS = SCSI$K_WRITE | SCSI$K_FL_ENAB_DIS;
301 /* Issue the SCSI Command. */
302 Status = sys$qiow(MK_EFN, DeviceFD, IO$_DIAGNOSE, &cmd_iosb, 0, 0,
303 &cmd_desc, sizeof(cmd_desc), 0, 0, 0, 0);
304 Result = SCSI$K_GOOD;
306 Status = cmd_iosb.SCSI$W_VMS_STAT;
308 Result = cmd_iosb.SCSI$B_IOSB_STS;
309 if (Result != SCSI$K_GOOD)
311 unsigned char RequestSenseCDB[6] =
312 { 0x03 /* REQUEST_SENSE */, 0, 0, 0, sizeof(RequestSense_T), 0 };
313 printf("SCSI command error: %d - requesting sense data\n", Result);
314 /* Execute Request Sense to determine the failure reason. */
315 ots$move5(0, 0, 0, sizeof(cmd_desc), &cmd_desc);
316 cmd_desc.SCSI$L_OPCODE = 1; /* Only defined SCSI opcode... a VMS thing */
317 cmd_desc.SCSI$L_FLAGS = SCSI$K_READ | SCSI$K_FL_ENAB_DIS;
318 cmd_desc.SCSI$A_CMD_ADDR = RequestSenseCDB;
319 cmd_desc.SCSI$L_CMD_LEN = 6;
320 cmd_desc.SCSI$A_DATA_ADDR = RequestSense;
321 cmd_desc.SCSI$L_DATA_LEN = sizeof(RequestSense_T);
322 cmd_desc.SCSI$L_PH_CH_TMOUT = 180;
323 cmd_desc.SCSI$L_DISCON_TMOUT = 180;
324 /* Issue the QIO to send the Request Sense Command. */
325 Status = sys$qiow(MK_EFN, DeviceFD, IO$_DIAGNOSE, &cmd_iosb, 0, 0,
326 &cmd_desc, sizeof(cmd_desc), 0, 0, 0, 0);
327 if (FailureStatusP(Status))
329 printf("?Error returned from REQUEST_SENSE(1): %%X%08lX\n", Status);
332 /* Check the VMS Status from QIO. */
333 Status = cmd_iosb.SCSI$W_VMS_STAT;
334 if (FailureStatusP(Status))
336 printf("?Error returned from REQUEST_SENSE(2): %%X%08lX\n", Status);
344 static void VMS_DefineStatusSymbols(void)
346 char SymbolName[32], SymbolValue[32];
347 int StorageElementNumber;
348 if (DataTransferElementFull)
350 /* Define MTX_DTE Symbol (environment variable) as 'FULL:n'. */
351 sprintf(SymbolValue, "FULL:%d",
352 DataTransferElementSourceStorageElementNumber);
353 lib$set_symbol(descr("MTX_DTE"), descr(SymbolValue), &2);
357 /* Define MTX_DTE Symbol (environment variable) as 'EMPTY'. */
358 lib$set_symbol(descr("MTX_DTE"), descr("EMPTY"), &2);
360 /* Define MTX_MSZ Symbol (environment variable) "Magazine SiZe" as 'n'. */
361 sprintf(SymbolValue, "%d", StorageElementCount);
362 lib$set_symbol(descr("MTX_MSZ"), descr(SymbolValue), &2);
363 for (StorageElementNumber = 1;
364 StorageElementNumber <= StorageElementCount;
365 StorageElementNumber++)
367 sprintf(SymbolName, "MTX_STE%02d", StorageElementNumber);
369 (StorageElementFull[StorageElementNumber] ? "FULL" : "EMPTY"));
370 lib$set_symbol(descr(SymbolName), descr(SymbolValue), &2);