2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-2000 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: scsi-linux.c,v 1.28 2006/03/09 20:06:10 johnfranks Exp $
29 * Interface to execute SCSI commands on Linux
31 * Copyright (c) Thomas Hepper th@ant.han.de
41 #ifdef HAVE_LINUX_LIKE_SCSI
50 #ifdef HAVE_SYS_TYPES_H
51 #include <sys/types.h>
53 #ifdef HAVE_SYS_STAT_H
66 #ifdef HAVE_SCSI_SCSI_IOCTL_H
67 #include <scsi/scsi_ioctl.h>
75 #ifdef HAVE_SYS_MTIO_H
79 #include <scsi-defs.h>
82 void SCSI_OS_Version()
85 static char rcsid[] = "$Id: scsi-linux.c,v 1.28 2006/03/09 20:06:10 johnfranks Exp $";
86 DebugPrint(DEBUG_ERROR, SECTION_INFO, "scsi-os-layer: %s\n",rcsid);
90 int SCSI_CloseDevice(int DeviceFD)
92 extern OpenFiles_T *pDev;
95 if (pDev[DeviceFD].devopen == 1)
97 pDev[DeviceFD].devopen = 0;
98 ret = close(pDev[DeviceFD].fd);
104 /* Open a device to talk to an scsi device, either per ioctl, or
111 * Define some readable defs for the falgs which can be set (like in the AIX dreiver)
115 int SCSI_OpenDevice(int ip)
117 extern OpenFiles_T *pDev;
122 char *buffer = NULL ; /* Will contain the device name after checking */
123 int openmode = O_RDONLY;
125 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### START SCSI_OpenDevice\n");
126 if (pDev[ip].inqdone == 0)
128 pDev[ip].inqdone = 1;
129 if (strncmp("/dev/sg", pDev[ip].dev, 7) != 0) /* Check if no sg device for an link .... */
131 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : checking if %s is a sg device\n", pDev[ip].dev);
132 if (lstat(pDev[ip].dev, &pstat) != -1)
134 if (S_ISLNK(pstat.st_mode) == 1)
136 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : is a link, checking destination\n");
137 if ((buffer = (char *)malloc(512)) == NULL)
139 DebugPrint(DEBUG_ERROR, SECTION_SCSI,"SCSI_OpenDevice : malloc failed\n");
142 memset(buffer, 0, 512);
143 if (( i = readlink(pDev[ip].dev, buffer, 512)) == -1)
145 if (errno == ENAMETOOLONG )
153 if (strncmp("/dev/sg", buffer, 7) == 0)
155 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : link points to %s\n", buffer) ;
159 } else {/* S_ISLNK(pstat.st_mode) == 1 */
160 DebugPrint(DEBUG_INFO, SECTION_SCSI,"No link %s\n", pDev[ip].dev) ;
161 buffer = stralloc(pDev[ip].dev);
163 } else {/* lstat(DeviceName, &pstat) != -1 */
164 DebugPrint(DEBUG_ERROR, SECTION_SCSI,"can't stat device %s\n", pDev[ip].dev);
168 buffer = stralloc(pDev[ip].dev);
172 if (pDev[ip].flags == 1)
177 DebugPrint(DEBUG_INFO, SECTION_SCSI,"Try to open %s\n", buffer);
178 if ((DeviceFD = open(buffer, openmode)) >= 0)
181 pDev[ip].devopen = 1;
182 pDev[ip].fd = DeviceFD;
184 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice open failed\n");
189 DebugPrint(DEBUG_INFO, SECTION_SCSI,"done\n");
190 if ( pDev[ip].flags == 1)
195 pDev[ip].dev = buffer;
196 if (pDev[ip].SCSI == 1)
198 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : use SG interface\n");
199 if ((timeout = ioctl(pDev[ip].fd, SG_GET_TIMEOUT)) > 0)
201 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : current timeout %d\n", timeout);
203 if (ioctl(pDev[ip].fd, SG_SET_TIMEOUT, &timeout) == 0)
205 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : timeout set to %d\n", timeout);
208 pDev[ip].inquiry = (SCSIInquiry_T *)malloc(INQUIRY_SIZE);
209 if (SCSI_Inquiry(ip, pDev[ip].inquiry, INQUIRY_SIZE) == 0)
211 if (pDev[ip].inquiry->type == TYPE_TAPE || pDev[ip].inquiry->type == TYPE_CHANGER)
214 pDev[ip].ident[i] = pDev[ip].inquiry->prod_ident[i];
215 for (i=15; i >= 0 && !isalnum(pDev[ip].ident[i]); i--)
217 pDev[ip].ident[i] = '\0';
221 if (pDev[ip].inquiry->type == TYPE_TAPE)
223 pDev[ip].type = stralloc("tape");
226 if (pDev[ip].inquiry->type == TYPE_CHANGER)
228 pDev[ip].type = stralloc("changer");
231 PrintInquiry(pDev[ip].inquiry);
232 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice (1)\n");
236 amfree(pDev[ip].inquiry);
237 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice (0)\n");
242 pDev[ip].devopen = 0;
244 amfree(pDev[ip].inquiry);
245 pDev[ip].inquiry = NULL;
246 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice (1)\n");
249 } else /* if (pDev[ip].SCSI == 1) */ {
250 DebugPrint(DEBUG_INFO, SECTION_SCSI,"Device not capable for SCSI commands\n");
252 pDev[ip].devopen = 0;
254 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice (1)\n");
257 } else { /* if (pDev[ip].inqdone == 0) */
258 if (pDev[ip].flags == 1)
264 if ((DeviceFD = open(pDev[ip].dev, openmode)) >= 0)
266 pDev[ip].devopen = 1;
267 pDev[ip].fd = DeviceFD;
268 if (pDev[ip].flags == 1)
270 if ((timeout = ioctl(pDev[ip].fd, SG_GET_TIMEOUT)) > 0)
272 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : current timeout %d\n", timeout);
274 if (ioctl(pDev[ip].fd, SG_SET_TIMEOUT, &timeout) == 0)
276 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : timeout set to %d\n", timeout);
280 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice (1)\n");
283 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice open failed\n");
287 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice should not happen !!\n");
291 #define SCSI_OFF sizeof(struct sg_header)
292 int SCSI_ExecuteCommand(int DeviceFD,
293 Direction_T Direction,
297 int DataBufferLength,
299 int RequestSenseLength)
301 extern OpenFiles_T *pDev;
302 struct sg_header *psg_header;
307 if (pDev[DeviceFD].avail == 0)
312 if (pDev[DeviceFD].devopen == 0)
313 if (SCSI_OpenDevice(DeviceFD) == 0)
316 if (SCSI_OFF + CDB_Length + DataBufferLength > 4096)
318 SCSI_CloseDevice(DeviceFD);
322 buffer = (char *)malloc(SCSI_OFF + CDB_Length + DataBufferLength);
325 dbprintf(("SCSI_ExecuteCommand memory allocation failure.\n"));
326 SCSI_CloseDevice(DeviceFD);
329 memset(buffer, 0, SCSI_OFF + CDB_Length + DataBufferLength);
330 memcpy(buffer + SCSI_OFF, CDB, CDB_Length);
332 psg_header = (struct sg_header *)buffer;
333 if (CDB_Length >= 12)
335 psg_header->twelve_byte = 1;
337 psg_header->twelve_byte = 0;
339 psg_header->result = 0;
340 psg_header->reply_len = SCSI_OFF + DataBufferLength;
348 osize = DataBufferLength;
352 DecodeSCSI(CDB, "SCSI_ExecuteCommand : ");
354 status = write(pDev[DeviceFD].fd, buffer, SCSI_OFF + CDB_Length + osize);
355 if ( status < 0 || status != SCSI_OFF + CDB_Length + osize ||
358 dbprintf(("SCSI_ExecuteCommand error send \n"));
359 SCSI_CloseDevice(DeviceFD);
364 memset(buffer, 0, SCSI_OFF + DataBufferLength);
365 status = read(pDev[DeviceFD].fd, buffer, SCSI_OFF + DataBufferLength);
366 memset(pRequestSense, 0, RequestSenseLength);
367 memcpy(pRequestSense, psg_header->sense_buffer, 16);
369 if ( status < 0 || status != SCSI_OFF + DataBufferLength ||
372 dbprintf(("SCSI_ExecuteCommand error read \n"));
373 dbprintf(("Status %d (%d) %2X\n", status, SCSI_OFF + DataBufferLength,psg_header->result ));
374 SCSI_CloseDevice(DeviceFD);
379 if (DataBufferLength)
381 memcpy(DataBuffer, buffer + SCSI_OFF, DataBufferLength);
384 SCSI_CloseDevice(DeviceFD);
391 static inline int min(int x, int y)
393 return (x < y ? x : y);
397 static inline int max(int x, int y)
399 return (x > y ? x : y);
402 int SCSI_OpenDevice(int ip)
404 extern OpenFiles_T *pDev;
408 if (pDev[ip].inqdone == 0)
410 pDev[ip].inqdone = 1;
411 if ((DeviceFD = open(pDev[ip].dev, O_RDWR)) >= 0)
414 pDev[ip].fd = DeviceFD;
416 pDev[ip].inquiry = (SCSIInquiry_T *)malloc(INQUIRY_SIZE);
417 dbprintf(("SCSI_OpenDevice : use ioctl interface\n"));
418 if (SCSI_Inquiry(ip, pDev[ip].inquiry, INQUIRY_SIZE) == 0)
420 if (pDev[ip].inquiry->type == TYPE_TAPE || pDev[ip].inquiry->type == TYPE_CHANGER)
422 for (i=0;i < 16 && pDev[ip].inquiry->prod_ident[i] != ' ';i++)
423 pDev[ip].ident[i] = pDev[ip].inquiry->prod_ident[i];
424 pDev[ip].ident[i] = '\0';
426 PrintInquiry(pDev[ip].inquiry);
429 amfree(pDev[ip].inquiry);
435 amfree(pDev[ip].inquiry);
436 pDev[ip].inquiry = NULL;
442 if ((DeviceFD = open(pDev[ip].dev, O_RDWR)) >= 0)
444 pDev[ip].fd = DeviceFD;
445 pDev[ip].devopen = 1;
448 pDev[ip].devopen = 0;
454 int SCSI_ExecuteCommand(int DeviceFD,
455 Direction_T Direction,
459 int DataBufferLength,
461 int RequestSenseLength)
463 extern OpenFiles_T *pDev;
464 unsigned char *Command;
465 int Zero = 0, Result;
467 if (pDev[DeviceFD].avail == 0)
472 if (pDev[DeviceFD].devopen == 0)
474 if (SCSI_OpenDevice(DeviceFD) == 0)
478 memset(pRequestSense, 0, RequestSenseLength);
482 Command = (unsigned char *)
483 malloc(8 + max(DataBufferLength, RequestSenseLength));
484 memcpy(&Command[0], &Zero, 4);
485 memcpy(&Command[4], &DataBufferLength, 4);
486 memcpy(&Command[8], CDB, CDB_Length);
489 Command = (unsigned char *)
490 malloc(8 + max(CDB_Length + DataBufferLength, RequestSenseLength));
491 memcpy(&Command[0], &DataBufferLength, 4);
492 memcpy(&Command[4], &Zero, 4);
493 memcpy(&Command[8], CDB, CDB_Length);
494 memcpy(&Command[8 + CDB_Length], DataBuffer, DataBufferLength);
498 DecodeSCSI(CDB, "SCSI_ExecuteCommand : ");
500 Result = ioctl(pDev[DeviceFD].fd, SCSI_IOCTL_SEND_COMMAND, Command);
502 memcpy(pRequestSense, &Command[8], RequestSenseLength);
503 else if (Direction == Input)
504 memcpy(DataBuffer, &Command[8], DataBufferLength);
506 SCSI_CloseDevice(DeviceFD);
521 * Send the command to the device with the
524 int Tape_Ioctl( int DeviceFD, int command)
526 extern OpenFiles_T *pDev;
530 if (pDev[DeviceFD].devopen == 0)
532 if (SCSI_OpenDevice(DeviceFD) == 0)
546 if (ioctl(pDev[DeviceFD].fd , MTIOCTOP, &mtop) != 0)
548 dbprintf(("Tape_Ioctl error ioctl %d\n",errno));
549 SCSI_CloseDevice(DeviceFD);
553 SCSI_CloseDevice(DeviceFD);
557 int Tape_Status( int DeviceFD)
559 extern OpenFiles_T *pDev;
563 if (pDev[DeviceFD].devopen == 0)
565 if (SCSI_OpenDevice(DeviceFD) == 0)
569 if (ioctl(pDev[DeviceFD].fd , MTIOCGET, &mtget) != 0)
571 DebugPrint(DEBUG_ERROR, SECTION_TAPE,"Tape_Status error ioctl %d\n",errno);
572 SCSI_CloseDevice(DeviceFD);
576 DebugPrint(DEBUG_INFO, SECTION_TAPE,"ioctl -> mtget.mt_gstat %lX\n",mtget.mt_gstat);
577 if (GMT_ONLINE(mtget.mt_gstat))
582 if (GMT_BOT(mtget.mt_gstat))
584 ret = ret | TAPE_BOT;
587 if (GMT_EOT(mtget.mt_gstat))
589 ret = ret | TAPE_EOT;
592 if (GMT_WR_PROT(mtget.mt_gstat))
594 ret = ret | TAPE_WR_PROT;
597 if (GMT_DR_OPEN(mtget.mt_gstat))
599 ret = ret | TAPE_NOT_LOADED;
602 SCSI_CloseDevice(DeviceFD);
607 * This functions scan all /dev/sg* devices
608 * It opens the device an print the result of the inquiry
611 int ScanBus(int print)
614 struct dirent *dirent;
615 extern OpenFiles_T *pDev;
619 if ((dir = opendir("/dev/")) == NULL)
621 dbprintf(("/dev/ error: %s", strerror(errno)));
625 while ((dirent = readdir(dir)) != NULL)
627 if (strstr(dirent->d_name, "sg") != NULL)
629 pDev[count].dev = malloc(10);
630 pDev[count].inqdone = 0;
631 sprintf(pDev[count].dev,"/dev/%s", dirent->d_name);
632 if (OpenDevice(count,pDev[count].dev, "Scan", NULL ))
634 SCSI_CloseDevice(count);
635 pDev[count].inqdone = 0;
639 printf("name /dev/%s ", dirent->d_name);
641 switch (pDev[count].inquiry->type)
674 printf("unknown %d",pDev[count].inquiry->type);
680 printf("Count %d\n",count);
682 amfree(pDev[count].dev);
683 pDev[count].dev=NULL;
692 * indent-tabs-mode: nil