Imported Upstream version 1.2.16rel
[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   return d_ret;
145 }
146
147
148 static int SCSI_OpenDevice(char *DeviceName)
149 {
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;
155   int DeviceFD = 0;
156   struct itmlst_3 {
157     unsigned short ilen;
158     unsigned short code;
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 },
164                      { 0, 0, 0, 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))
170     {
171       VMS_ExitCode = Status;
172       FatalError("cannot allocate device '%s' - %X\n", DeviceName, Status);
173     }
174   Status = sys$assign(descr(DeviceName), &DeviceFD, 0, 0);
175   if (FailureStatusP(Status))
176     {
177       VMS_ExitCode = Status;
178       FatalError("cannot open device '%s' - %X\n", DeviceName, Status);
179     }
180   Status = sys$getdviw(0, DeviceFD, 0, &dvi_itmlst, &iosb, 0, 0, 0);
181   if (FailureStatusP(Status))
182     {
183       VMS_ExitCode = Status;
184       FatalError("cannot $getdvi(1) on device '%s' - %X\n", DeviceName, Status);
185     }
186   if (FailureStatusP(Status = iosb[0]))
187     {
188       VMS_ExitCode = Status;
189       FatalError("cannot $getdvi(2) on device '%s' - %X\n", DeviceName, Status);
190     }
191   if (dvcls != DC$_TAPE)
192     {
193       VMS_ExitCode = SS$_IVDEVNAM;
194       FatalError("specified device is NOT a magtape: operation denied\n");
195     }
196 #ifndef __DECC
197 #ifndef DEV$M_SCSI
198 #define DEV$M_SCSI 0x1000000
199 #endif
200 #endif
201   if (~dchr2 & DEV$M_SCSI)
202     {
203       VMS_ExitCode = SS$_IVDEVNAM;
204       FatalError("specified magtape is NOT a SCSI device: operation denied\n");
205     }
206 #if USING_DEC_DRIVE | USING_LDRSET
207 #ifndef __DECC
208 #ifndef DEV$M_LDR
209 #define DEV$M_LDR 0x100000
210 #endif
211 #endif
212   if (~dchr2 & DEV$M_LDR)
213     {
214       VMS_ExitCode = SS$_IVDEVNAM;
215       FatalError(
216         "specified SCSI magtape does not have a loader: operation denied\n");
217     }
218 #endif
219   if (ismnt)
220     {
221       VMS_ExitCode = SS$_DEVMOUNT;
222       FatalError("specified device is mounted: operation denied\n");
223     }
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))
230     {
231       VMS_ExitCode = Status;
232       FatalError(
233         "error enabling privs (diagnose,log_io,phy_io): operation denied\n");
234     }
235   Status = sys$setprv(1, 0, 0, &setprivs);
236   if (FailureStatusP(Status))
237     {
238       VMS_ExitCode = Status;
239       FatalError("error retrieving current privs: operation denied\n");
240     }
241   if (!setprivs.prv$v_diagnose)
242     {
243       VMS_ExitCode = SS$_NODIAGNOSE;
244       FatalError("DIAGNOSE privilege is required: operation denied\n");
245     }
246   if (!setprivs.prv$v_phy_io && !setprivs.prv$v_log_io)
247     {
248       VMS_ExitCode = SS$_NOPHY_IO;
249       FatalError("PHY_IO or LOG_IO privilege is required: operation denied\n");
250     }
251   return DeviceFD;
252 }
253
254
255 static void SCSI_CloseDevice(char *DeviceName,
256                              int DeviceFD)
257 {
258   unsigned long Status;
259   Status = sys$dassgn(DeviceFD);
260   if (FailureStatusP(Status))
261     FatalError("cannot close SCSI device '%s' - %X\n", DeviceName, Status);
262 }
263
264
265 static int SCSI_ExecuteCommand(int DeviceFD,
266                                Direction_T Direction,
267                                CDB_T *CDB,
268                                int CDB_Length,
269                                void *DataBuffer,
270                                int DataBufferLength,
271                                RequestSense_T *RequestSense)
272 {
273   SCSI$DESC cmd_desc;
274   SCSI$IOSB cmd_iosb;
275   unsigned long Status;
276   int Result;
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) */
288   switch (Direction)
289     {
290       /*
291         NOTE: Do NOT include flag SCSI$K_FL_ENAB_SYNC.
292               It does NOT work for this case.
293       */
294     case Input:
295       cmd_desc.SCSI$L_FLAGS = SCSI$K_READ | SCSI$K_FL_ENAB_DIS;
296       break;
297     case Output:
298       cmd_desc.SCSI$L_FLAGS = SCSI$K_WRITE | SCSI$K_FL_ENAB_DIS;
299       break;
300     }
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;
305   if (Status & 1)
306     Status = cmd_iosb.SCSI$W_VMS_STAT;
307   if (Status & 1)
308     Result = cmd_iosb.SCSI$B_IOSB_STS;
309   if (Result != SCSI$K_GOOD)
310     {
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))
328         {
329           printf("?Error returned from REQUEST_SENSE(1): %%X%08lX\n", Status);
330           sys$exit(Status);
331         }
332       /* Check the VMS Status from QIO. */
333       Status = cmd_iosb.SCSI$W_VMS_STAT;
334       if (FailureStatusP(Status))
335         {
336           printf("?Error returned from REQUEST_SENSE(2): %%X%08lX\n", Status);
337           sys$exit(Status);
338         }
339     }
340   return Result;
341 }
342
343
344 static void VMS_DefineStatusSymbols(void)
345 {
346   char SymbolName[32], SymbolValue[32];
347   int StorageElementNumber;
348   if (DataTransferElementFull)
349     {
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);
354     }
355   else
356     {
357       /* Define MTX_DTE Symbol (environment variable) as 'EMPTY'. */
358       lib$set_symbol(descr("MTX_DTE"), descr("EMPTY"), &2);
359     }
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++)
366     {
367       sprintf(SymbolName, "MTX_STE%02d", StorageElementNumber);
368       strcpy(SymbolValue,
369              (StorageElementFull[StorageElementNumber] ? "FULL" : "EMPTY"));
370       lib$set_symbol(descr(SymbolName), descr(SymbolValue), &2);
371     }
372 }