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