1 /* Copyright 1997, 1998 Leonard Zubkoff <lnz@dandelion.com>
2 Changes in Feb 2000 Eric Green <eric@estinc.com>
4 $Date: 2001/06/19 21:51:32 $
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.
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
18 #include <linux/param.h>
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.
26 #warning "HZ is not defined, mtx might not work correctly!"
30 /* These are copied out of BRU 16.1, with all the boolean masks changed
33 #define S_NO_SENSE(s) ((s)->SenseKey == 0x0)
34 #define S_RECOVERED_ERROR(s) ((s)->SenseKey == 0x1)
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)
43 #define DEFAULT_TIMEOUT 3*60 /* 3 minutes here */
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.
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)
56 #define STILL_A_VALID_READ(s) (HIT_FILEMARK(s) || SHORT_READ(s) || HIT_EOD(s) || HIT_EOP(s) || HIT_EOM(s))
59 #define SG_SCSI_DEFAULT_TIMEOUT SG_DEFAULT_TIMEOUT
61 DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
63 int timeout=SG_SCSI_DEFAULT_TIMEOUT; /* 5 minutes */
64 int DeviceFD = open(DeviceName, O_RDWR);
66 FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
68 if(ioctl(DeviceFD, SG_SET_TIMEOUT, &timeout)) {
69 FatalError("failed to set sg timeout - %m\n");
72 return (DEVICE_TYPE) DeviceFD;
75 static int sg_timeout = SG_SCSI_DEFAULT_TIMEOUT ;
77 void SCSI_Set_Timeout(int secs) {
81 void SCSI_Default_Timeout(void) {
82 sg_timeout=SG_SCSI_DEFAULT_TIMEOUT;
85 void SCSI_CloseDevice(char *DeviceName,
88 if (close(DeviceFD) < 0)
89 FatalError("cannot close SCSI device '%s' - %m\n", DeviceName);
93 /* Added by Eric Green <eric@estinc.com> to deal with burping
94 * Seagate autoloader (hopefully!).
96 /* Get the SCSI ID and LUN... */
97 scsi_id_t *SCSI_GetIDLun(DEVICE_TYPE fd) {
101 struct my_scsi_idlun {
106 status=ioctl(fd,SCSI_IOCTL_GET_IDLUN,&idlun);
108 return NULL; /* sorry! */
111 retval=(scsi_id_t *)xmalloc(sizeof(scsi_id_t));
112 retval->id=idlun.word1 & 0xff;
113 retval->lun=idlun.word1 >> 8 & 0xff;
115 fprintf(stderr,"SCSI:ID=%d LUN=%d\n",retval->id,retval->lun);
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....
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.
134 #ifndef OLD_EXECUTE_COMMAND_STUFF
136 static void slow_memcopy(unsigned char *src, unsigned char *dest, int numbytes)
144 int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
145 Direction_T Direction,
149 int DataBufferLength,
150 RequestSense_T *RequestSense)
153 unsigned char *Command=NULL; /* the command data struct sent to them... */
154 unsigned char *ResultBuf=NULL; /* the data we read in return... */
156 unsigned char *src; /* for copying stuff, sigh. */
157 unsigned char *dest; /* for copy stuff, again, sigh. */
159 int write_length = sizeof(struct sg_header)+CDB_Length;
160 int i; /* a random index... */
161 int result; /* the result of the write... */
164 struct sg_header *Header; /* we actually point this into Command... */
165 struct sg_header *ResultHeader; /* we point this into ResultBuf... */
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: */
172 fprintf(stderr,"Setting timeout to %d\n", sg_timeout);
175 if(ioctl(DeviceFD, SG_SET_TIMEOUT, &sg_timeout)) {
176 FatalError("failed to set sg timeout - %m\n");
180 if (Direction == Output) { /* if we're writing, our length is longer... */
181 write_length += DataBufferLength;
184 /* allocate some memory... enough for the command plus the header +
185 * any other data that we may need here...
188 Command=(unsigned char *)xmalloc(write_length);
189 Header = (struct sg_header *) Command; /* make it point to start of buf */
191 dest=Command; /* now to copy the CDB... from start of buffer,*/
192 dest+= sizeof(struct sg_header); /* increment it past the header. */
194 slow_memcopy((char *)CDB,dest,CDB_Length);
196 /* if we are writing additional data, tack it on here! */
197 if (Direction == Output) {
199 slow_memcopy(DataBuffer,dest,DataBufferLength); /* copy to end of command */
201 /* Now to fill in the Header struct: */
202 Header->reply_len=DataBufferLength+sizeof(struct sg_header);
204 fprintf(stderr,"sg:reply_len(sent)=%d\n",Header->reply_len);
206 Header->twelve_byte = CDB_Length == 12;
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? */
213 /* Now to do the write... */
214 result=write(DeviceFD,Command,write_length);
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.
220 if ( (result < 0) || (result != write_length) || Header->result ||
221 Header->sense_buffer[0] ) {
223 fprintf(stderr,"scsi:result=%d Header->result=%d Header->sense_buffer[0]=%d\n",
224 result,Header->result,Header->sense_buffer[0]);
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));
232 dest=(unsigned char *)RequestSense;
233 *dest=(unsigned char)Header->result; /* may chop, sigh... */
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.
242 if (!STILL_A_VALID_READ(RequestSense)) {
243 free(Command); /* zap memory leak, sigh */
244 /* okay, find us a non-zero value to return :-(. */
247 } else if (Header->result) {
248 return Header->result;
250 return -1; /* sigh */
256 result=0; /* we're okay! */
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);
265 ResultHeader=(struct sg_header *)ResultBuf;
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! */
276 fprintf(stderr,"sg:Reading %d bytes from DeviceFD\n",Header->reply_len);
279 result=read(DeviceFD,ResultBuf,Header->reply_len);
281 fprintf(stderr,"sg:result=%d ResultHeader->result=%d\n",
282 result,ResultHeader->result);
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] ) {
290 "scsi: result=%d Header->reply_len=%d ResultHeader->result=%d ResultHeader->sense_buffer[0]=%d\n",
293 ResultHeader->result,
294 ResultHeader->sense_buffer[0]);
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.
304 if (!STILL_A_VALID_READ(RequestSense)) {
309 } else if (ResultHeader->result) {
311 return ResultHeader->result;
314 return -1; /* sigh! */
317 result=-1; /* if it was a valid read, still have -1 result. */
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 */
327 fprintf(stderr,"Setting timeout to %d\n", sg_timeout);
330 /* if not default, set it: */
331 if(ioctl(DeviceFD, SG_SET_TIMEOUT, &sg_timeout)) {
332 FatalError("failed to set sg timeout - %m\n");
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) {
341 fprintf(stderr,"Header->reply_len=%d,ResultHeader->reply_len=%d\n",
342 Header->reply_len,ResultHeader->reply_len);
344 src=ResultBuf+sizeof(struct sg_header);
346 for (i=0;i< (ResultHeader->reply_len);i++) {
347 if (i>=DataBufferLength) break; /* eep! */
353 free(Command); /* clean up memory leak... */
355 return result; /* good stuff ! */
361 #ifdef OLD_EXECUTE_COMMAND_STUFF
363 int SCSI_ExecuteCommand(int DeviceFD,
364 Direction_T Direction,
368 int DataBufferLength,
369 RequestSense_T *RequestSense)
371 unsigned char *Command;
372 int Zero = 0, Result;
373 memset(RequestSense, 0, sizeof(RequestSense_T));
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);
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);
392 Result = ioctl(DeviceFD, SCSI_IOCTL_SEND_COMMAND, Command);
394 memcpy(RequestSense, &Command[8], sizeof(RequestSense_T));
395 else if (Direction == Input)
396 memcpy(DataBuffer, &Command[8], DataBufferLength);