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 /* 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.
26 /* These are copied out of BRU 16.1, with all the boolean masks changed
29 #define S_NO_SENSE(s) ((s)->SenseKey == 0x0)
30 #define S_RECOVERED_ERROR(s) ((s)->SenseKey == 0x1)
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)
39 #define DEFAULT_TIMEOUT 3*60 /* 3 minutes here */
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.
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)
52 #define STILL_A_VALID_READ(s) (HIT_FILEMARK(s) || SHORT_READ(s) || HIT_EOD(s) || HIT_EOP(s) || HIT_EOM(s))
55 #define SG_SCSI_DEFAULT_TIMEOUT HZ*60*5 /* 5 minutes? */
57 DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
59 int timeout=SG_SCSI_DEFAULT_TIMEOUT; /* 5 minutes */
60 int DeviceFD = open(DeviceName, O_RDWR);
62 FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
64 if(ioctl(DeviceFD, SG_SET_TIMEOUT, &timeout)) {
65 FatalError("failed to set sg timeout - %m\n");
68 return (DEVICE_TYPE) DeviceFD;
71 static int sg_timeout = SG_SCSI_DEFAULT_TIMEOUT ;
73 void SCSI_Set_Timeout(int secs) {
77 void SCSI_Default_Timeout(void) {
78 sg_timeout=SG_SCSI_DEFAULT_TIMEOUT;
81 void SCSI_CloseDevice(char *DeviceName,
84 if (close(DeviceFD) < 0)
85 FatalError("cannot close SCSI device '%s' - %m\n", DeviceName);
89 /* Added by Eric Green <eric@estinc.com> to deal with burping
90 * Seagate autoloader (hopefully!).
92 /* Get the SCSI ID and LUN... */
93 scsi_id_t *SCSI_GetIDLun(DEVICE_TYPE fd) {
97 struct my_scsi_idlun {
102 status=ioctl(fd,SCSI_IOCTL_GET_IDLUN,&idlun);
104 return NULL; /* sorry! */
107 retval=(scsi_id_t *)xmalloc(sizeof(scsi_id_t));
108 retval->id=idlun.word1 & 0xff;
109 retval->lun=idlun.word1 >> 8 & 0xff;
111 fprintf(stderr,"SCSI:ID=%d LUN=%d\n",retval->id,retval->lun);
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....
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.
130 #ifndef OLD_EXECUTE_COMMAND_STUFF
132 static void slow_memcopy(unsigned char *src, unsigned char *dest, int numbytes)
140 int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
141 Direction_T Direction,
145 int DataBufferLength,
146 RequestSense_T *RequestSense)
149 unsigned char *Command=NULL; /* the command data struct sent to them... */
150 unsigned char *ResultBuf=NULL; /* the data we read in return... */
152 unsigned char *src; /* for copying stuff, sigh. */
153 unsigned char *dest; /* for copy stuff, again, sigh. */
155 int write_length = sizeof(struct sg_header)+CDB_Length;
156 int i; /* a random index... */
157 int result; /* the result of the write... */
160 struct sg_header *Header; /* we actually point this into Command... */
161 struct sg_header *ResultHeader; /* we point this into ResultBuf... */
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: */
168 fprintf(stderr,"Setting timeout to %d\n", sg_timeout);
171 if(ioctl(DeviceFD, SG_SET_TIMEOUT, &sg_timeout)) {
172 FatalError("failed to set sg timeout - %m\n");
176 if (Direction == Output) { /* if we're writing, our length is longer... */
177 write_length += DataBufferLength;
180 /* allocate some memory... enough for the command plus the header +
181 * any other data that we may need here...
184 Command=(unsigned char *)xmalloc(write_length);
185 Header = (struct sg_header *) Command; /* make it point to start of buf */
187 dest=Command; /* now to copy the CDB... from start of buffer,*/
188 dest+= sizeof(struct sg_header); /* increment it past the header. */
190 slow_memcopy((char *)CDB,dest,CDB_Length);
192 /* if we are writing additional data, tack it on here! */
193 if (Direction == Output) {
195 slow_memcopy(DataBuffer,dest,DataBufferLength); /* copy to end of command */
197 /* Now to fill in the Header struct: */
198 Header->reply_len=DataBufferLength+sizeof(struct sg_header);
200 fprintf(stderr,"sg:reply_len(sent)=%d\n",Header->reply_len);
202 Header->twelve_byte = CDB_Length == 12;
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? */
209 /* Now to do the write... */
210 result=write(DeviceFD,Command,write_length);
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.
216 if ( (result < 0) || (result != write_length) || Header->result ||
217 Header->sense_buffer[0] ) {
219 fprintf(stderr,"scsi:result=%d Header->result=%d Header->sense_buffer[0]=%d\n",
220 result,Header->result,Header->sense_buffer[0]);
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));
228 dest=(unsigned char *)RequestSense;
229 *dest=(unsigned char)Header->result; /* may chop, sigh... */
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.
238 if (!STILL_A_VALID_READ(RequestSense)) {
239 free(Command); /* zap memory leak, sigh */
240 /* okay, find us a non-zero value to return :-(. */
243 } else if (Header->result) {
244 return Header->result;
246 return -1; /* sigh */
252 result=0; /* we're okay! */
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);
261 ResultHeader=(struct sg_header *)ResultBuf;
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! */
272 fprintf(stderr,"sg:Reading %d bytes from DeviceFD\n",Header->reply_len);
275 result=read(DeviceFD,ResultBuf,Header->reply_len);
277 fprintf(stderr,"sg:result=%d ResultHeader->result=%d\n",
278 result,ResultHeader->result);
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] ) {
286 "scsi: result=%d Header->reply_len=%d ResultHeader->result=%d ResultHeader->sense_buffer[0]=%d\n",
289 ResultHeader->result,
290 ResultHeader->sense_buffer[0]);
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.
300 if (!STILL_A_VALID_READ(RequestSense)) {
305 } else if (ResultHeader->result) {
307 return ResultHeader->result;
310 return -1; /* sigh! */
313 result=-1; /* if it was a valid read, still have -1 result. */
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 */
323 fprintf(stderr,"Setting timeout to %d\n", sg_timeout);
326 /* if not default, set it: */
327 if(ioctl(DeviceFD, SG_SET_TIMEOUT, &sg_timeout)) {
328 FatalError("failed to set sg timeout - %m\n");
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) {
337 fprintf(stderr,"Header->reply_len=%d,ResultHeader->reply_len=%d\n",
338 Header->reply_len,ResultHeader->reply_len);
340 src=ResultBuf+sizeof(struct sg_header);
342 for (i=0;i< (ResultHeader->reply_len);i++) {
343 if (i>=DataBufferLength) break; /* eep! */
349 free(Command); /* clean up memory leak... */
351 return result; /* good stuff ! */
357 #ifdef OLD_EXECUTE_COMMAND_STUFF
359 int SCSI_ExecuteCommand(int DeviceFD,
360 Direction_T Direction,
364 int DataBufferLength,
365 RequestSense_T *RequestSense)
367 unsigned char *Command;
368 int Zero = 0, Result;
369 memset(RequestSense, 0, sizeof(RequestSense_T));
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);
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);
388 Result = ioctl(DeviceFD, SCSI_IOCTL_SEND_COMMAND, Command);
390 memcpy(RequestSense, &Command[8], sizeof(RequestSense_T));
391 else if (Direction == Input)
392 memcpy(DataBuffer, &Command[8], DataBufferLength);