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.30.2.1 2006/12/12 14:56:38 martinea 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>
81 extern OpenFiles_T *pDev;
83 void SCSI_OS_Version(void)
86 static char rcsid[] = "$Id: scsi-linux.c,v 1.30.2.1 2006/12/12 14:56:38 martinea Exp $";
87 DebugPrint(DEBUG_ERROR, SECTION_INFO, "scsi-os-layer: %s\n",rcsid);
91 int SCSI_CloseDevice(int DeviceFD)
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)
121 char *buffer = NULL ; /* Will contain the device name after checking */
122 int openmode = O_RDONLY;
124 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### START SCSI_OpenDevice\n");
125 if (pDev[ip].inqdone == 0)
127 pDev[ip].inqdone = 1;
128 if (strncmp("/dev/sg", pDev[ip].dev, 7) != 0) /* Check if no sg device for an link .... */
130 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : checking if %s is a sg device\n", pDev[ip].dev);
131 if (lstat(pDev[ip].dev, &pstat) != -1)
133 if (S_ISLNK(pstat.st_mode) == 1)
135 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : is a link, checking destination\n");
136 if ((buffer = (char *)malloc(513)) == NULL)
138 DebugPrint(DEBUG_ERROR, SECTION_SCSI,"SCSI_OpenDevice : malloc failed\n");
141 memset(buffer, 0, 513);
142 if (( i = readlink(pDev[ip].dev, buffer, 512)) == -1)
144 if (errno == ENAMETOOLONG )
152 if (strncmp("/dev/sg", buffer, 7) == 0)
154 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : link points to %s\n", buffer) ;
158 } else {/* S_ISLNK(pstat.st_mode) == 1 */
159 DebugPrint(DEBUG_INFO, SECTION_SCSI,"No link %s\n", pDev[ip].dev) ;
160 buffer = stralloc(pDev[ip].dev);
162 } else {/* lstat(DeviceName, &pstat) != -1 */
163 DebugPrint(DEBUG_ERROR, SECTION_SCSI,"can't stat device %s\n", pDev[ip].dev);
167 buffer = stralloc(pDev[ip].dev);
171 if (pDev[ip].flags == 1)
176 DebugPrint(DEBUG_INFO, SECTION_SCSI,"Try to open %s\n", buffer);
177 if ((DeviceFD = open(buffer, openmode)) >= 0)
180 pDev[ip].devopen = 1;
181 pDev[ip].fd = DeviceFD;
183 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice open failed\n");
188 DebugPrint(DEBUG_INFO, SECTION_SCSI,"done\n");
189 if ( pDev[ip].flags == 1)
194 pDev[ip].dev = buffer;
195 if (pDev[ip].SCSI == 1)
197 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : use SG interface\n");
198 if ((timeout = ioctl(pDev[ip].fd, SG_GET_TIMEOUT)) > 0)
200 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : current timeout %d\n", timeout);
202 if (ioctl(pDev[ip].fd, SG_SET_TIMEOUT, &timeout) == 0)
204 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : timeout set to %d\n", timeout);
207 pDev[ip].inquiry = (SCSIInquiry_T *)malloc(INQUIRY_SIZE);
208 if (SCSI_Inquiry(ip, pDev[ip].inquiry, (u_char)INQUIRY_SIZE) == 0)
210 if (pDev[ip].inquiry->type == TYPE_TAPE || pDev[ip].inquiry->type == TYPE_CHANGER)
213 pDev[ip].ident[i] = pDev[ip].inquiry->prod_ident[i];
214 for (i=15; i >= 0 && !isalnum(pDev[ip].ident[i]); i--)
216 pDev[ip].ident[i] = '\0';
220 if (pDev[ip].inquiry->type == TYPE_TAPE)
222 pDev[ip].type = stralloc("tape");
225 if (pDev[ip].inquiry->type == TYPE_CHANGER)
227 pDev[ip].type = stralloc("changer");
230 PrintInquiry(pDev[ip].inquiry);
231 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice (1)\n");
235 amfree(pDev[ip].inquiry);
236 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice (0)\n");
241 pDev[ip].devopen = 0;
243 amfree(pDev[ip].inquiry);
244 pDev[ip].inquiry = NULL;
245 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice (1)\n");
248 } else /* if (pDev[ip].SCSI == 1) */ {
249 DebugPrint(DEBUG_INFO, SECTION_SCSI,"Device not capable for SCSI commands\n");
251 pDev[ip].devopen = 0;
253 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice (1)\n");
256 } else { /* if (pDev[ip].inqdone == 0) */
257 if (pDev[ip].flags == 1)
263 if ((DeviceFD = open(pDev[ip].dev, openmode)) >= 0)
265 pDev[ip].devopen = 1;
266 pDev[ip].fd = DeviceFD;
267 if (pDev[ip].flags == 1)
269 if ((timeout = ioctl(pDev[ip].fd, SG_GET_TIMEOUT)) > 0)
271 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : current timeout %d\n", timeout);
273 if (ioctl(pDev[ip].fd, SG_SET_TIMEOUT, &timeout) == 0)
275 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : timeout set to %d\n", timeout);
279 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice (1)\n");
282 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice open failed\n");
286 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice should not happen !!\n");
290 #define SCSI_OFF SIZEOF(struct sg_header)
291 int SCSI_ExecuteCommand(int DeviceFD,
292 Direction_T Direction,
296 size_t DataBufferLength,
297 RequestSense_T *pRequestSense,
298 size_t RequestSenseLength)
300 struct sg_header *psg_header;
305 /* Basic sanity checks */
306 assert(CDB_Length <= UCHAR_MAX);
307 assert(RequestSenseLength <= UCHAR_MAX);
309 /* Clear buffer for cases where sense is not returned */
310 memset(pRequestSense, 0, RequestSenseLength);
312 if (pDev[DeviceFD].avail == 0)
317 if (pDev[DeviceFD].devopen == 0)
318 if (SCSI_OpenDevice(DeviceFD) == 0)
321 if (SCSI_OFF + CDB_Length + DataBufferLength > 4096)
323 SCSI_CloseDevice(DeviceFD);
327 buffer = (char *)malloc(SCSI_OFF + CDB_Length + DataBufferLength);
330 dbprintf(("SCSI_ExecuteCommand memory allocation failure.\n"));
331 SCSI_CloseDevice(DeviceFD);
334 memset(buffer, 0, SCSI_OFF + CDB_Length + DataBufferLength);
335 memcpy(buffer + SCSI_OFF, CDB, CDB_Length);
337 psg_header = (struct sg_header *)buffer;
338 if (CDB_Length >= 12)
340 psg_header->twelve_byte = 1;
342 psg_header->twelve_byte = 0;
344 psg_header->result = 0;
345 psg_header->reply_len = (int)(SCSI_OFF + DataBufferLength);
353 osize = DataBufferLength;
357 DecodeSCSI(CDB, "SCSI_ExecuteCommand : ");
359 status = write(pDev[DeviceFD].fd, buffer, SCSI_OFF + CDB_Length + osize);
360 if ( (status < (ssize_t)0) ||
361 (status != (ssize_t)(SCSI_OFF + CDB_Length + osize)) ||
362 (psg_header->result != 0))
364 dbprintf(("SCSI_ExecuteCommand error send \n"));
365 SCSI_CloseDevice(DeviceFD);
370 memset(buffer, 0, SCSI_OFF + DataBufferLength);
371 status = read(pDev[DeviceFD].fd, buffer, SCSI_OFF + DataBufferLength);
372 memset(pRequestSense, 0, RequestSenseLength);
373 memcpy(pRequestSense, psg_header->sense_buffer, 16);
376 (status != (ssize_t)(SCSI_OFF + DataBufferLength)) ||
377 (psg_header->result != 0))
379 dbprintf(("SCSI_ExecuteCommand error read \n"));
380 dbprintf(("Status %zd (%lu) %2X\n", status, SCSI_OFF + DataBufferLength,psg_header->result ));
381 SCSI_CloseDevice(DeviceFD);
386 if (DataBufferLength)
388 memcpy(DataBuffer, buffer + SCSI_OFF, DataBufferLength);
391 SCSI_CloseDevice(DeviceFD);
398 static inline int min(int x, int y)
400 return (x < y ? x : y);
404 static inline int max(int x, int y)
406 return (x > y ? x : y);
409 int SCSI_OpenDevice(int ip)
414 if (pDev[ip].inqdone == 0)
416 pDev[ip].inqdone = 1;
417 if ((DeviceFD = open(pDev[ip].dev, O_RDWR)) >= 0)
420 pDev[ip].fd = DeviceFD;
422 pDev[ip].inquiry = (SCSIInquiry_T *)malloc(INQUIRY_SIZE);
423 dbprintf(("SCSI_OpenDevice : use ioctl interface\n"));
424 if (SCSI_Inquiry(ip, pDev[ip].inquiry, (u_char)INQUIRY_SIZE) == 0)
426 if (pDev[ip].inquiry->type == TYPE_TAPE || pDev[ip].inquiry->type == TYPE_CHANGER)
428 for (i=0;i < 16 && pDev[ip].inquiry->prod_ident[i] != ' ';i++)
429 pDev[ip].ident[i] = pDev[ip].inquiry->prod_ident[i];
430 pDev[ip].ident[i] = '\0';
432 PrintInquiry(pDev[ip].inquiry);
435 amfree(pDev[ip].inquiry);
441 amfree(pDev[ip].inquiry);
442 pDev[ip].inquiry = NULL;
448 if ((DeviceFD = open(pDev[ip].dev, O_RDWR)) >= 0)
450 pDev[ip].fd = DeviceFD;
451 pDev[ip].devopen = 1;
454 pDev[ip].devopen = 0;
460 int SCSI_ExecuteCommand(int DeviceFD,
461 Direction_T Direction,
465 int DataBufferLength,
466 RequestSense_T *pRequestSense,
467 int RequestSenseLength)
469 unsigned char *Command;
470 int Zero = 0, Result;
472 if (pDev[DeviceFD].avail == 0)
477 if (pDev[DeviceFD].devopen == 0)
479 if (SCSI_OpenDevice(DeviceFD) == 0)
483 memset(pRequestSense, 0, RequestSenseLength);
487 Command = (unsigned char *)
488 malloc(8 + max(DataBufferLength, RequestSenseLength));
489 memcpy(&Command[0], &Zero, 4);
490 memcpy(&Command[4], &DataBufferLength, 4);
491 memcpy(&Command[8], CDB, CDB_Length);
494 Command = (unsigned char *)
495 malloc(8 + max(CDB_Length + DataBufferLength, RequestSenseLength));
496 memcpy(&Command[0], &DataBufferLength, 4);
497 memcpy(&Command[4], &Zero, 4);
498 memcpy(&Command[8], CDB, CDB_Length);
499 memcpy(&Command[8 + CDB_Length], DataBuffer, DataBufferLength);
503 DecodeSCSI(CDB, "SCSI_ExecuteCommand : ");
505 Result = ioctl(pDev[DeviceFD].fd, SCSI_IOCTL_SEND_COMMAND, Command);
507 memcpy(pRequestSense, &Command[8], RequestSenseLength);
508 else if (Direction == Input)
509 memcpy(DataBuffer, &Command[8], DataBufferLength);
511 SCSI_CloseDevice(DeviceFD);
526 * Send the command to the device with the
529 int Tape_Ioctl( int DeviceFD, int command)
534 if (pDev[DeviceFD].devopen == 0)
536 if (SCSI_OpenDevice(DeviceFD) == 0)
550 if (ioctl(pDev[DeviceFD].fd , MTIOCTOP, &mtop) != 0)
552 dbprintf(("Tape_Ioctl error ioctl %s\n",strerror(errno)));
553 SCSI_CloseDevice(DeviceFD);
557 SCSI_CloseDevice(DeviceFD);
561 int Tape_Status( int DeviceFD)
566 memset(&mtget, 0, SIZEOF(mtget));
567 if (pDev[DeviceFD].devopen == 0)
569 if (SCSI_OpenDevice(DeviceFD) == 0)
573 if (ioctl(pDev[DeviceFD].fd , MTIOCGET, &mtget) != 0)
575 DebugPrint(DEBUG_ERROR, SECTION_TAPE,"Tape_Status error ioctl %s\n",
577 SCSI_CloseDevice(DeviceFD);
581 DebugPrint(DEBUG_INFO, SECTION_TAPE,"ioctl -> mtget.mt_gstat %lX\n",mtget.mt_gstat);
582 if (GMT_ONLINE(mtget.mt_gstat))
587 if (GMT_BOT(mtget.mt_gstat))
589 ret = ret | TAPE_BOT;
592 if (GMT_EOT(mtget.mt_gstat))
594 ret = ret | TAPE_EOT;
597 if (GMT_WR_PROT(mtget.mt_gstat))
599 ret = ret | TAPE_WR_PROT;
602 if (GMT_DR_OPEN(mtget.mt_gstat))
604 ret = ret | TAPE_NOT_LOADED;
607 SCSI_CloseDevice(DeviceFD);
612 * This functions scan all /dev/sg* devices
613 * It opens the device an print the result of the inquiry
616 int ScanBus(int print)
619 struct dirent *dirent;
622 if ((dir = opendir("/dev/")) == NULL)
624 dbprintf(("/dev/ error: %s", strerror(errno)));
628 while ((dirent = readdir(dir)) != NULL)
630 if (strstr(dirent->d_name, "sg") != NULL)
632 pDev[count].dev = malloc(10);
633 pDev[count].inqdone = 0;
634 snprintf(pDev[count].dev, SIZEOF(pDev[count].dev),
635 "/dev/%s", dirent->d_name);
636 if (OpenDevice(count,pDev[count].dev, "Scan", NULL ))
638 SCSI_CloseDevice(count);
639 pDev[count].inqdone = 0;
643 printf("name /dev/%s ", dirent->d_name);
645 switch (pDev[count].inquiry->type)
678 printf("unknown %d",pDev[count].inquiry->type);
684 printf("Count %d\n",count);
686 amfree(pDev[count].dev);
687 pDev[count].dev=NULL;
696 * indent-tabs-mode: nil