Imported Upstream version 1.2.16rel
[debian/mtx] / scsi_linux.c
1 /* Copyright 1997, 1998 Leonard Zubkoff <lnz@dandelion.com>
2    Changes in Feb 2000 Eric Green <eric@estinc.com>
3
4 $Date: 2001/06/19 21:51:32 $
5 $Revision: 1.2 $
6
7   This program is free software; you may redistribute and/or modify it under
8   the terms of the GNU General Public License Version 2 as published by the
9   Free Software Foundation.
10
11   This program is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   for complete details.
15
16 */
17
18 /* this is the SCSI commands for Linux. Note that <eric@estinc.com> changed 
19  * it from using SCSI_IOCTL_SEND_COMMAND to using the SCSI generic interface.
20  */
21
22 #ifndef HZ
23 #define HZ 100
24 #endif
25
26 /* These are copied out of BRU 16.1, with all the boolean masks changed
27  * to our bitmasks.
28 */
29 #define S_NO_SENSE(s) ((s)->SenseKey == 0x0)
30 #define S_RECOVERED_ERROR(s) ((s)->SenseKey == 0x1)
31
32 #define S_NOT_READY(s) ((s)->SenseKey == 0x2)
33 #define S_MEDIUM_ERROR(s) ((s)->SenseKey == 0x3)
34 #define S_HARDWARE_ERROR(s) ((s)->SenseKey == 0x4)
35 #define S_UNIT_ATTENTION(s) ((s)->SenseKey == 0x6)
36 #define S_BLANK_CHECK(s) ((s)->SenseKey == 0x8)
37 #define S_VOLUME_OVERFLOW(s) ((s)->SenseKey == 0xd)
38
39 #define DEFAULT_TIMEOUT 3*60  /* 3 minutes here */
40
41 /* Sigh, the T-10 SSC spec says all of the following is needed to
42  * detect a short read while in variable block mode, and that even
43  * though we got a BLANK_CHECK or MEDIUM_ERROR, it's still a valid read.
44  */
45
46 #define HIT_FILEMARK(s) (S_NO_SENSE((s)) && (s)->Filemark && (s)->Valid)
47 #define SHORT_READ(s) (S_NO_SENSE((s)) && (s)->ILI && (s)->Valid &&  (s)->AdditionalSenseCode==0  && (s)->AdditionalSenseCodeQualifier==0)
48 #define HIT_EOD(s) (S_BLANK_CHECK((s)) && (s)->Valid)
49 #define HIT_EOP(s) (S_MEDIUM_ERROR((s)) && (s)->EOM && (s)->Valid)
50 #define HIT_EOM(s) ((s)->EOM && (s)->Valid)
51
52 #define STILL_A_VALID_READ(s) (HIT_FILEMARK(s) || SHORT_READ(s) || HIT_EOD(s) || HIT_EOP(s) || HIT_EOM(s))
53
54
55 #define SG_SCSI_DEFAULT_TIMEOUT HZ*60*5  /* 5 minutes? */
56
57 DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
58 {
59   int timeout=SG_SCSI_DEFAULT_TIMEOUT;      /* 5 minutes */
60   int DeviceFD = open(DeviceName, O_RDWR);
61   if (DeviceFD < 0)
62     FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
63
64   if(ioctl(DeviceFD, SG_SET_TIMEOUT, &timeout)) {
65     FatalError("failed to set sg timeout - %m\n");
66   }
67
68   return (DEVICE_TYPE) DeviceFD;
69 }
70
71 static int sg_timeout = SG_SCSI_DEFAULT_TIMEOUT ;
72
73 void SCSI_Set_Timeout(int secs) {
74   sg_timeout=secs*HZ;
75 }
76  
77 void SCSI_Default_Timeout(void) {
78   sg_timeout=SG_SCSI_DEFAULT_TIMEOUT;
79 }
80
81 void SCSI_CloseDevice(char *DeviceName,
82                              DEVICE_TYPE DeviceFD)
83 {
84   if (close(DeviceFD) < 0)
85     FatalError("cannot close SCSI device '%s' - %m\n", DeviceName);
86 }
87
88
89 /* Added by Eric Green <eric@estinc.com> to deal with burping
90  * Seagate autoloader (hopefully!). 
91  */
92 /* Get the SCSI ID and LUN... */
93 scsi_id_t *SCSI_GetIDLun(DEVICE_TYPE fd) {
94   int status;
95   scsi_id_t *retval;
96   
97   struct my_scsi_idlun {
98     int word1;
99     int word2;
100   } idlun;
101
102   status=ioctl(fd,SCSI_IOCTL_GET_IDLUN,&idlun);
103   if (status) {
104     return NULL; /* sorry! */
105   }
106   
107   retval=(scsi_id_t *)xmalloc(sizeof(scsi_id_t));
108   retval->id=idlun.word1 & 0xff;
109   retval->lun=idlun.word1 >> 8 & 0xff;
110 #ifdef DEBUG
111   fprintf(stderr,"SCSI:ID=%d LUN=%d\n",retval->id,retval->lun);
112 #endif
113   return retval;
114 }
115   
116
117 /* Changed February 2000 by Eric Green <eric@estinc.com> to 
118  * use the SCSI generic interface rather than SCSI_IOCTL_SEND_COMMAND
119  * so that we can get more than PAGE_SIZE data....
120  *
121  * Note that the SCSI generic interface abuses READ and WRITE calls to serve
122  * the same purpose as IOCTL calls, i.e., for "writes", the contents of the
123  * buffer that you send as the argument to the write() call are actually
124  * altered to fill in result status and sense data (if needed). 
125  * Also note that this brain-dead interface does not have any sort of 
126  * provisions for expanding the sg_header struct in a backward-compatible
127  * manner. This sucks. But sucks less than SCSI_IOCTL_SEND_COMMAND, sigh.
128  */
129
130 #ifndef OLD_EXECUTE_COMMAND_STUFF
131
132 static void slow_memcopy(unsigned char *src, unsigned char *dest, int numbytes)
133 {
134   while (numbytes--) {
135     *dest++ = *src++;
136   }
137 }
138
139
140 int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
141                                Direction_T Direction,
142                                CDB_T *CDB,
143                                int CDB_Length,
144                                void *DataBuffer,
145                                int DataBufferLength,
146                                RequestSense_T *RequestSense)
147 {
148
149   unsigned char *Command=NULL;   /* the command data struct sent to them... */
150   unsigned char *ResultBuf=NULL; /* the data we read in return...         */
151
152   unsigned char *src;       /* for copying stuff, sigh. */
153   unsigned char *dest;      /* for copy stuff, again, sigh. */
154
155   int write_length = sizeof(struct sg_header)+CDB_Length;
156   int i;                  /* a random index...          */
157   int result;             /* the result of the write... */ 
158   
159   
160   struct sg_header *Header; /* we actually point this into Command... */
161   struct sg_header *ResultHeader; /* we point this into ResultBuf... */
162
163
164   /* First, see if we need to set our SCSI timeout to something different */
165   if (sg_timeout != SG_SCSI_DEFAULT_TIMEOUT) {
166     /* if not default, set it: */
167 #ifdef DEBUG_TIMEOUT
168     fprintf(stderr,"Setting timeout to %d\n", sg_timeout);
169     fflush(stderr);
170 #endif
171     if(ioctl(DeviceFD, SG_SET_TIMEOUT, &sg_timeout)) {
172       FatalError("failed to set sg timeout - %m\n");
173     }
174   }
175
176   if (Direction == Output) {  /* if we're writing, our length is longer... */
177     write_length += DataBufferLength; 
178   }
179   
180   /* allocate some memory... enough for the command plus the header +
181    *  any other data that we may need here...
182    */
183   
184   Command=(unsigned char *)xmalloc(write_length);
185   Header = (struct sg_header *) Command;  /* make it point to start of buf */
186
187   dest=Command; /* now to copy the CDB... from start of buffer,*/
188   dest+= sizeof(struct sg_header); /* increment it past the header. */
189
190   slow_memcopy((char *)CDB,dest,CDB_Length);
191
192   /* if we are writing additional data, tack it on here! */
193   if (Direction == Output) {
194     dest += CDB_Length;
195     slow_memcopy(DataBuffer,dest,DataBufferLength); /* copy to end of command */
196   }
197   /* Now to fill in the Header struct: */
198   Header->reply_len=DataBufferLength+sizeof(struct sg_header);
199 #ifdef DEBUG
200   fprintf(stderr,"sg:reply_len(sent)=%d\n",Header->reply_len);
201 #endif
202   Header->twelve_byte = CDB_Length == 12; 
203   Header->result = 0;
204   Header->pack_len = write_length; /* # of bytes written... */
205   Header->pack_id = 0;             /* not used              */
206   Header->other_flags = 0;         /* not used.             */
207   Header->sense_buffer[0]=0;      /* used? */
208
209   /* Now to do the write... */
210   result=write(DeviceFD,Command,write_length);
211   
212   /* Now to check the result :-(. */
213   /* Note that we don't have any request sense here. So we have no
214    * idea what's going on. 
215    */ 
216   if ( (result < 0) || (result != write_length) || Header->result ||
217        Header->sense_buffer[0] ) {
218 #ifdef DEBUG_SCSI
219     fprintf(stderr,"scsi:result=%d Header->result=%d Header->sense_buffer[0]=%d\n",
220             result,Header->result,Header->sense_buffer[0]);
221 #endif  
222     /* we don't have any real sense data, sigh :-(. */
223     if (Header->sense_buffer[0]) {
224       /* well, I guess we DID have some! eep! copy the sense data! */
225       slow_memcopy((char *)Header->sense_buffer,(char *)RequestSense,
226                    sizeof(Header->sense_buffer));
227     } else {
228       dest=(unsigned char *)RequestSense;
229       *dest=(unsigned char)Header->result; /* may chop, sigh... */
230     }
231
232     /* okay, now, we may or may not need to find a non-zero value to return.
233      * For tape drives, we may get a BLANK_CHECK or MEDIUM_ERROR and find
234      * that it's *STILL* a good read! Use the STILL_A_VALID_READ macro
235      * that calls all those macros I cribbed from Richard. 
236      */
237     
238     if (!STILL_A_VALID_READ(RequestSense)) {
239       free(Command); /* zap memory leak, sigh */
240       /* okay, find us a non-zero value to return :-(. */
241       if (result) {
242         return result;
243       } else if (Header->result) {
244         return Header->result;
245       } else {
246         return -1;  /* sigh */
247       }
248     } else {
249       result=-1;
250     }
251   } else {
252     result=0; /* we're okay! */
253   }
254     
255   
256   /* now to allocate the new block.... */
257   ResultBuf=(unsigned char *)xmalloc(Header->reply_len);
258   /* now to clear ResultBuf... */
259   slow_bzero(ResultBuf,Header->reply_len); 
260
261   ResultHeader=(struct sg_header *)ResultBuf;
262   
263   /* copy the original Header... */
264   ResultHeader->result=0;
265   ResultHeader->pack_id=0;
266   ResultHeader->other_flags=0;
267   ResultHeader->reply_len=Header->reply_len;
268   ResultHeader->twelve_byte = CDB_Length == 12; 
269   ResultHeader->pack_len = write_length; /* # of bytes written... */
270   ResultHeader->sense_buffer[0]=0; /* whoops! Zero that! */
271 #ifdef DEBUG
272   fprintf(stderr,"sg:Reading %d bytes from DeviceFD\n",Header->reply_len);
273   fflush(stderr);
274 #endif
275   result=read(DeviceFD,ResultBuf,Header->reply_len);
276 #ifdef DEBUG
277   fprintf(stderr,"sg:result=%d ResultHeader->result=%d\n",
278           result,ResultHeader->result);
279   fflush(stderr);
280 #endif
281   /* New: added check to see if the result block is still all zeros! */
282   if ( (result < 0) || (result != Header->reply_len) || ResultHeader->result ||
283        ResultHeader->sense_buffer[0] ) {
284 #ifdef DEBUG
285     fprintf(stderr,
286             "scsi: result=%d Header->reply_len=%d ResultHeader->result=%d ResultHeader->sense_buffer[0]=%d\n",
287             result,
288             Header->reply_len,
289             ResultHeader->result,
290             ResultHeader->sense_buffer[0]);
291 #endif
292     /* eep! copy the sense data! */
293     slow_memcopy((char *)ResultHeader->sense_buffer,(char *)RequestSense,
294                  sizeof(ResultHeader->sense_buffer));
295     /* sense data copied, now find us a non-zero value to return :-(. */
296     /* NOTE: Some commands return sense data even though they validly
297      * executed! We catch a few of those with the macro STILL_A_VALID_READ.
298      */
299
300     if (!STILL_A_VALID_READ(RequestSense)) {
301       free(Command);
302       if (result) {
303         free(ResultBuf);
304         return result;
305       } else if (ResultHeader->result) {
306         free(ResultBuf);
307         return ResultHeader->result;
308       } else {
309         free(ResultBuf);
310         return -1; /* sigh! */
311       } 
312     } else {
313       result=-1; /* if it was a valid read, still have -1 result. */
314     }
315   } else {
316     result=0;
317   }
318   
319   /* See if we need to reset our SCSI timeout */
320   if (sg_timeout != SG_SCSI_DEFAULT_TIMEOUT) {
321     sg_timeout = SG_SCSI_DEFAULT_TIMEOUT; /* reset it back to default */
322 #ifdef DEBUG_TIMEOUT
323     fprintf(stderr,"Setting timeout to %d\n", sg_timeout);
324     fflush(stderr);
325 #endif
326     /* if not default, set it: */
327     if(ioctl(DeviceFD, SG_SET_TIMEOUT, &sg_timeout)) {
328       FatalError("failed to set sg timeout - %m\n");
329     }
330   }
331   
332   
333   /* now for the crowning moment: copying any result into the DataBuffer! */
334   /* (but only if it were an input command and not an output command :-}  */
335   if (Direction == Input) {
336 #ifdef DEBUG
337     fprintf(stderr,"Header->reply_len=%d,ResultHeader->reply_len=%d\n",
338             Header->reply_len,ResultHeader->reply_len);
339 #endif
340     src=ResultBuf+sizeof(struct sg_header);
341     dest=DataBuffer;
342     for (i=0;i< (ResultHeader->reply_len);i++) {
343       if (i>=DataBufferLength) break;  /* eep! */
344       *dest++=*src++;
345     }
346   }
347   
348   /* and return! */
349   free(Command);    /* clean up memory leak... */
350   free(ResultBuf);
351   return result; /* good stuff ! */
352 }
353
354 #endif  
355   
356
357 #ifdef OLD_EXECUTE_COMMAND_STUFF
358
359 int SCSI_ExecuteCommand(int DeviceFD,
360                                Direction_T Direction,
361                                CDB_T CDB,
362                                int CDB_Length,
363                                void *DataBuffer,
364                                int DataBufferLength,
365                                RequestSense_T *RequestSense)
366 {
367   unsigned char *Command;
368   int Zero = 0, Result;
369   memset(RequestSense, 0, sizeof(RequestSense_T));
370   switch (Direction)
371     {
372     case Input:
373       Command = (unsigned char *)
374         xmalloc(8 + max(DataBufferLength, sizeof(RequestSense_T)));
375       memcpy(&Command[0], &Zero, 4);
376       memcpy(&Command[4], &DataBufferLength, 4);
377       memcpy(&Command[8], CDB, CDB_Length);
378       break;
379     case Output:
380       Command = (unsigned char *)
381         xmalloc(8 + max(CDB_Length + DataBufferLength, sizeof(RequestSense_T)));
382       memcpy(&Command[0], &DataBufferLength, 4);
383       memcpy(&Command[4], &Zero, 4);
384       memcpy(&Command[8], CDB, CDB_Length);
385       memcpy(&Command[8 + CDB_Length], DataBuffer, DataBufferLength);
386       break;
387     }
388   Result = ioctl(DeviceFD, SCSI_IOCTL_SEND_COMMAND, Command);
389   if (Result != 0)
390     memcpy(RequestSense, &Command[8], sizeof(RequestSense_T));
391   else if (Direction == Input)
392     memcpy(DataBuffer, &Command[8], DataBufferLength);
393   free(Command);
394   return Result;
395 }
396
397 #endif