update stds version
[debian/mtx] / vms / scsi.c
1 /* SCSI.C - VMS-specific SCSI routines.
2 **
3 ** TECSys Development, Inc., April 1998
4 **
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.
10 **
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....
14 */
15
16 /* The remainder of these credits are included directly from the CDWRITE20
17 ** sources. */
18
19 /* Copyright 1994 Yggdrasil Computing, Inc. */
20 /* Written by Adam J. Richter (adam@yggdrasil.com) */
21
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 */
25
26 /*
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.
30 */
31
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).
35
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.
40
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.  */
44
45 /* The second notice comes from sys$examples:gktest.c (OpenVMS 7.0)*/
46
47 /*
48 ** COPYRIGHT (c) 1993 BY
49 ** DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS.
50 ** ALL RIGHTS RESERVED.
51 **
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
57 ** TRANSFERRED.
58 **
59 ** THE INFORMATION IN THIS SOFTWARE IS  SUBJECT TO CHANGE WITHOUT NOTICE
60 ** AND  SHOULD  NOT  BE  CONSTRUED  AS A COMMITMENT BY DIGITAL EQUIPMENT
61 ** CORPORATION.
62 **
63 ** DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE  OR  RELIABILITY OF ITS
64 ** SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.
65 */
66
67 /*
68   Define the Generic SCSI Command Descriptor.
69 */
70
71 typedef struct scsi$desc
72 {
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 */
88 }
89 SCSI$DESC;
90
91
92 /*
93   Define the SCSI Input/Output Status Block.
94 */
95
96 typedef struct scsi$iosb
97 {
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 */
102 }
103 SCSI$IOSB;
104
105
106 /*
107   Define the VMS symbolic representation for a successful SCSI command.
108 */
109
110 #define SCSI$K_GOOD             0
111
112
113 /*
114   Define the SCSI Flag Field Constants.
115 */
116
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 */
120
121
122 /*
123   Define DESCR_CNT.  It must be a power of two and large enough
124   for the maximum concurrent usage of descriptors.
125 */
126
127 #define DESCR_CNT 16
128
129
130 #define MK_EFN                  0   /* Event Flag Number */
131 #define FailureStatusP(Status)  (~(Status) & 1)
132
133
134 static struct dsc$descriptor_s *descr(char *String)
135 {
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;
144
145         return d_ret;
146 }
147
148
149 static int SCSI_OpenDevice(char *DeviceName)
150 {
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;
156         int DeviceFD = 0;
157         struct itmlst_3
158         {
159                 unsigned short ilen;
160                 unsigned short code;
161                 unsigned long *returnP;
162                 unsigned long ignored;
163         }
164         dvi_itmlst[] = {
165                 { 4, DVI$_MNT, 0 /*&ismnt*/, 0 },
166                 { 4, DVI$_DEVCLASS, 0 /*&dvcls*/, 0 },
167                 { 4, DVI$_DEVCHAR2, 0 /*&dchr2*/, 0 },
168                 { 0, 0, 0, 0 }
169         };
170
171         dvi_itmlst[0].returnP = &ismnt;
172         dvi_itmlst[1].returnP = &dvcls;
173         dvi_itmlst[2].returnP = &dchr2;
174
175         Status = sys$alloc(descr(DeviceName), 0, 0, 0, 0);
176
177         if (FailureStatusP(Status))
178         {
179                 VMS_ExitCode = Status;
180                 FatalError("cannot allocate device '%s' - %X\n", DeviceName, Status);
181         }
182
183         Status = sys$assign(descr(DeviceName), &DeviceFD, 0, 0);
184         if (FailureStatusP(Status))
185         {
186                 VMS_ExitCode = Status;
187                 FatalError("cannot open device '%s' - %X\n", DeviceName, Status);
188         }
189
190         Status = sys$getdviw(0, DeviceFD, 0, &dvi_itmlst, &iosb, 0, 0, 0);
191         if (FailureStatusP(Status))
192         {
193                 VMS_ExitCode = Status;
194                 FatalError("cannot $getdvi(1) on device '%s' - %X\n", DeviceName, Status);
195         }
196
197         if (FailureStatusP(Status = iosb[0]))
198         {
199                 VMS_ExitCode = Status;
200                 FatalError("cannot $getdvi(2) on device '%s' - %X\n", DeviceName, Status);
201         }
202
203         if (dvcls != DC$_TAPE)
204         {
205                 VMS_ExitCode = SS$_IVDEVNAM;
206                 FatalError("specified device is NOT a magtape: operation denied\n");
207         }
208 #ifndef __DECC
209 #ifndef DEV$M_SCSI
210 #define DEV$M_SCSI 0x1000000
211 #endif
212 #endif
213         if (~dchr2 & DEV$M_SCSI)
214         {
215                 VMS_ExitCode = SS$_IVDEVNAM;
216                 FatalError("specified magtape is NOT a SCSI device: operation denied\n");
217         }
218 #if USING_DEC_DRIVE | USING_LDRSET
219 #ifndef __DECC
220 #ifndef DEV$M_LDR
221 #define DEV$M_LDR 0x100000
222 #endif
223 #endif
224         if (~dchr2 & DEV$M_LDR)
225         {
226                 VMS_ExitCode = SS$_IVDEVNAM;
227                 FatalError("specified SCSI magtape does not have a loader: operation denied\n");
228         }
229 #endif
230         if (ismnt)
231         {
232                 VMS_ExitCode = SS$_DEVMOUNT;
233                 FatalError("specified device is mounted: operation denied\n");
234         }
235
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);
241
242         if (FailureStatusP(Status))
243         {
244                 VMS_ExitCode = Status;
245                 FatalError("error enabling privs (diagnose,log_io,phy_io): operation denied\n");
246         }
247
248         Status = sys$setprv(1, 0, 0, &setprivs);
249         if (FailureStatusP(Status))
250         {
251                 VMS_ExitCode = Status;
252                 FatalError("error retrieving current privs: operation denied\n");
253         }
254
255         if (!setprivs.prv$v_diagnose)
256         {
257                 VMS_ExitCode = SS$_NODIAGNOSE;
258                 FatalError("DIAGNOSE privilege is required: operation denied\n");
259         }
260
261         if (!setprivs.prv$v_phy_io && !setprivs.prv$v_log_io)
262         {
263                 VMS_ExitCode = SS$_NOPHY_IO;
264                 FatalError("PHY_IO or LOG_IO privilege is required: operation denied\n");
265         }
266
267         return DeviceFD;
268 }
269
270
271 static void SCSI_CloseDevice(char *DeviceName, int DeviceFD)
272 {
273         unsigned long Status;
274
275         Status = sys$dassgn(DeviceFD);
276         if (FailureStatusP(Status))
277                 FatalError("cannot close SCSI device '%s' - %X\n", DeviceName, Status);
278 }
279
280
281 static int SCSI_ExecuteCommand( int DeviceFD,
282                                                                 Direction_T Direction,
283                                                                 CDB_T *CDB,
284                                                                 int CDB_Length,
285                                                                 void *DataBuffer,
286                                                                 int DataBufferLength,
287                                                                 RequestSense_T *RequestSense)
288 {
289         SCSI$DESC cmd_desc;
290         SCSI$IOSB cmd_iosb;
291         unsigned long Status;
292         int Result;
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) */
304
305         switch (Direction)
306         {
307         /*
308         NOTE: Do NOT include flag SCSI$K_FL_ENAB_SYNC.
309         It does NOT work for this case.
310         */
311         case Input:
312                 cmd_desc.SCSI$L_FLAGS = SCSI$K_READ | SCSI$K_FL_ENAB_DIS;
313                 break;
314
315         case Output:
316                 cmd_desc.SCSI$L_FLAGS = SCSI$K_WRITE | SCSI$K_FL_ENAB_DIS;
317                 break;
318         }
319
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;
324
325         if (Status & 1)
326                 Status = cmd_iosb.SCSI$W_VMS_STAT;
327
328         if (Status & 1)
329                 Result = cmd_iosb.SCSI$B_IOSB_STS;
330
331         if (Result != SCSI$K_GOOD)
332         {
333                 unsigned char RequestSenseCDB[6] =
334                         { 0x03 /* REQUEST_SENSE */, 0, 0, 0, sizeof(RequestSense_T), 0 };
335
336                 printf("SCSI command error: %d - requesting sense data\n", Result);
337
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;
348
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))
353                 {
354                         printf("?Error returned from REQUEST_SENSE(1): %%X%08lX\n", Status);
355                         sys$exit(Status);
356                 }
357
358                 /* Check the VMS Status from QIO. */
359                 Status = cmd_iosb.SCSI$W_VMS_STAT;
360                 if (FailureStatusP(Status))
361                 {
362                         printf("?Error returned from REQUEST_SENSE(2): %%X%08lX\n", Status);
363                         sys$exit(Status);
364                 }
365         }
366         return Result;
367 }
368
369
370 static void VMS_DefineStatusSymbols(void)
371 {
372         char SymbolName[32], SymbolValue[32];
373         int StorageElementNumber;
374
375         if (DataTransferElementFull)
376         {
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);
381         }
382         else
383         {
384                 /* Define MTX_DTE Symbol (environment variable) as 'EMPTY'. */
385                 lib$set_symbol(descr("MTX_DTE"), descr("EMPTY"), &2);
386         }
387
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++)
394         {
395                 sprintf(SymbolName, "MTX_STE%02d", StorageElementNumber);
396                 strcpy(SymbolValue,
397                         (StorageElementFull[StorageElementNumber] ? "FULL" : "EMPTY"));
398                 lib$set_symbol(descr(SymbolName), descr(SymbolValue), &2);
399         }
400 }