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.1.2.18.4.1.2.5 2003/07/05 16:59:01 ant 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.1.2.18.4.1.2.5 2003/07/05 16:59:01 ant 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;
121 int sg_info = 0; /* Used to get some infos about the sg interface */
122 int ret = 0; /* To store return results from ioctl etc */
124 char *buffer = NULL ; /* Will contain the device name after checking */
125 int openmode = O_RDONLY;
127 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### START SCSI_OpenDevice\n");
128 if (pDev[ip].inqdone == 0)
130 pDev[ip].inqdone = 1;
131 if (strncmp("/dev/sg", pDev[ip].dev, 7) != 0) /* Check if no sg device for an link .... */
133 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : checking if %s is a sg device\n", pDev[ip].dev);
134 if (lstat(pDev[ip].dev, &pstat) != -1)
136 if (S_ISLNK(pstat.st_mode) == 1)
138 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : is a link, checking destination\n");
139 if ((buffer = (char *)malloc(512)) == NULL)
141 DebugPrint(DEBUG_ERROR, SECTION_SCSI,"SCSI_OpenDevice : malloc failed\n");
144 memset(buffer, 0, 512);
145 if (( i = readlink(pDev[ip].dev, buffer, 512)) == -1)
147 if (errno == ENAMETOOLONG )
155 if (strncmp("/dev/sg", buffer, 7) == 0)
157 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : link points to %s\n", buffer) ;
161 } else {/* S_ISLNK(pstat.st_mode) == 1 */
162 DebugPrint(DEBUG_INFO, SECTION_SCSI,"No link %s\n", pDev[ip].dev) ;
163 buffer = stralloc(pDev[ip].dev);
165 } else {/* lstat(DeviceName, &pstat) != -1 */
166 DebugPrint(DEBUG_ERROR, SECTION_SCSI,"can't stat device %s\n", pDev[ip].dev);
170 buffer = stralloc(pDev[ip].dev);
174 if (pDev[ip].flags == 1)
179 DebugPrint(DEBUG_INFO, SECTION_SCSI,"Try to open %s\n", buffer);
180 if ((DeviceFD = open(buffer, openmode)) > 0)
183 pDev[ip].devopen = 1;
184 pDev[ip].fd = DeviceFD;
186 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice open failed\n");
190 DebugPrint(DEBUG_INFO, SECTION_SCSI,"done\n");
191 if ( pDev[ip].flags == 1)
196 pDev[ip].dev = stralloc(buffer);
197 if (pDev[ip].SCSI == 1)
199 if ((ret = ioctl(DeviceFD, SG_GET_VERSION_NUM, &sg_info)) == 0)
201 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : SG_VERSION %d\n",sg_info);
203 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : SG_VERSION ioctl returned %d\n", ret);
206 if ((ret = ioctl(DeviceFD, SG_GET_RESERVED_SIZE, &sg_info)) == 0)
208 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : SG_RESERVED_SIZE %d\n",sg_info);
210 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : SG_RESERVED_SIZE ioctl returned %d\n", ret);
213 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : use SG interface\n");
214 if ((timeout = ioctl(pDev[ip].fd, SG_GET_TIMEOUT)) > 0)
216 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : current timeout %d\n", timeout);
218 if (ioctl(pDev[ip].fd, SG_SET_TIMEOUT, &timeout) == 0)
220 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : timeout set to %d\n", timeout);
223 pDev[ip].inquiry = (SCSIInquiry_T *)malloc(INQUIRY_SIZE);
224 if (SCSI_Inquiry(ip, pDev[ip].inquiry, INQUIRY_SIZE) == 0)
226 if (pDev[ip].inquiry->type == TYPE_TAPE || pDev[ip].inquiry->type == TYPE_CHANGER)
229 pDev[ip].ident[i] = pDev[ip].inquiry->prod_ident[i];
230 for (i=15; i >= 0 && !isalnum(pDev[ip].ident[i]); i--)
232 pDev[ip].ident[i] = '\0';
236 if (pDev[ip].inquiry->type == TYPE_TAPE)
238 pDev[ip].type = stralloc("tape");
241 if (pDev[ip].inquiry->type == TYPE_CHANGER)
243 pDev[ip].type = stralloc("changer");
246 PrintInquiry(pDev[ip].inquiry);
247 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice (1)\n");
251 free(pDev[ip].inquiry);
252 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice (0)\n");
257 pDev[ip].devopen = 0;
259 free(pDev[ip].inquiry);
260 pDev[ip].inquiry = NULL;
261 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice (1)\n");
264 } else /* if (pDev[ip].SCSI == 1) */ {
265 DebugPrint(DEBUG_INFO, SECTION_SCSI,"Device not capable for SCSI commands\n");
267 pDev[ip].devopen = 0;
269 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice (1)\n");
272 } else { /* if (pDev[ip].inqdone == 0) */
273 if (pDev[ip].flags == 1)
279 if ((DeviceFD = open(pDev[ip].dev, openmode)) > 0)
281 pDev[ip].devopen = 1;
282 pDev[ip].fd = DeviceFD;
283 if (pDev[ip].flags == 1)
285 if ((timeout = ioctl(pDev[ip].fd, SG_GET_TIMEOUT)) > 0)
287 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : current timeout %d\n", timeout);
289 if (ioctl(pDev[ip].fd, SG_SET_TIMEOUT, &timeout) == 0)
291 DebugPrint(DEBUG_INFO, SECTION_SCSI,"SCSI_OpenDevice : timeout set to %d\n", timeout);
295 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice (1)\n");
298 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice open failed\n");
302 DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice should not happen !!\n");
306 #define SCSI_OFF sizeof(struct sg_header)
307 int SCSI_ExecuteCommand(int DeviceFD,
308 Direction_T Direction,
312 int DataBufferLength,
314 int RequestSenseLength)
316 extern OpenFiles_T *pDev;
317 struct sg_header *psg_header;
322 if (pDev[DeviceFD].avail == 0)
327 if (pDev[DeviceFD].devopen == 0)
329 SCSI_OpenDevice(DeviceFD);
332 /* if (SCSI_OFF + CDB_Length + DataBufferLength > 4096) */
334 /* SCSI_CloseDevice(DeviceFD); */
335 /* DebugPrint(DEBUG_ERROR, SECTION_SCSI,"##### SCSI_ExecuteCommand error, SCSI_OFF + CDB_Length + DataBufferLength > 4096\n"); */
339 buffer = (char *)malloc(SCSI_OFF + CDB_Length + DataBufferLength);
340 memset(buffer, 0, SCSI_OFF + CDB_Length + DataBufferLength);
341 memcpy(buffer + SCSI_OFF, CDB, CDB_Length);
343 psg_header = (struct sg_header *)buffer;
344 if (CDB_Length >= 12)
346 psg_header->twelve_byte = 1;
348 psg_header->twelve_byte = 0;
350 psg_header->result = 0;
351 psg_header->reply_len = SCSI_OFF + DataBufferLength;
359 osize = DataBufferLength;
363 DecodeSCSI(CDB, "SCSI_ExecuteCommand : ");
365 status = write(pDev[DeviceFD].fd, buffer, SCSI_OFF + CDB_Length + osize);
366 if ( status < 0 || status != SCSI_OFF + CDB_Length + osize ||
369 dbprintf(("SCSI_ExecuteCommand error send \n"));
370 SCSI_CloseDevice(DeviceFD);
374 memset(buffer, 0, SCSI_OFF + DataBufferLength);
375 status = read(pDev[DeviceFD].fd, buffer, SCSI_OFF + DataBufferLength);
376 memset(pRequestSense, 0, RequestSenseLength);
377 memcpy(pRequestSense, psg_header->sense_buffer, 16);
379 if ( status < 0 || status != SCSI_OFF + DataBufferLength ||
382 dbprintf(("SCSI_ExecuteCommand error read \n"));
383 dbprintf(("Status %d (%d) %2X\n", status, SCSI_OFF + DataBufferLength, psg_header->result ));
384 SCSI_CloseDevice(DeviceFD);
388 if (DataBufferLength)
390 memcpy(DataBuffer, buffer + SCSI_OFF, DataBufferLength);
394 SCSI_CloseDevice(DeviceFD);
400 static inline int min(int x, int y)
402 return (x < y ? x : y);
406 static inline int max(int x, int y)
408 return (x > y ? x : y);
411 int SCSI_OpenDevice(int ip)
413 extern OpenFiles_T *pDev;
417 if (pDev[ip].inqdone == 0)
419 pDev[ip].inqdone = 1;
420 if ((DeviceFD = open(pDev[ip].dev, O_RDWR)) > 0)
423 pDev[ip].fd = DeviceFD;
425 pDev[ip].inquiry = (SCSIInquiry_T *)malloc(INQUIRY_SIZE);
426 dbprintf(("SCSI_OpenDevice : use ioctl interface\n"));
427 if (SCSI_Inquiry(ip, pDev[ip].inquiry, INQUIRY_SIZE) == 0)
429 if (pDev[ip].inquiry->type == TYPE_TAPE || pDev[ip].inquiry->type == TYPE_CHANGER)
431 for (i=0;i < 16 && pDev[ip].inquiry->prod_ident[i] != ' ';i++)
432 pDev[ip].ident[i] = pDev[ip].inquiry->prod_ident[i];
433 pDev[ip].ident[i] = '\0';
435 PrintInquiry(pDev[ip].inquiry);
438 free(pDev[ip].inquiry);
444 free(pDev[ip].inquiry);
445 pDev[ip].inquiry = NULL;
451 if ((DeviceFD = open(pDev[ip].dev, O_RDWR)) > 0)
453 pDev[ip].fd = DeviceFD;
454 pDev[ip].devopen = 1;
457 pDev[ip].devopen = 0;
463 int SCSI_ExecuteCommand(int DeviceFD,
464 Direction_T Direction,
468 int DataBufferLength,
470 int RequestSenseLength)
472 extern OpenFiles_T *pDev;
473 unsigned char *Command;
474 int Zero = 0, Result;
476 if (pDev[DeviceFD].avail == 0)
481 if (pDev[DeviceFD].devopen == 0)
483 SCSI_OpenDevice(DeviceFD);
486 memset(pRequestSense, 0, RequestSenseLength);
490 Command = (unsigned char *)
491 malloc(8 + max(DataBufferLength, RequestSenseLength));
492 memcpy(&Command[0], &Zero, 4);
493 memcpy(&Command[4], &DataBufferLength, 4);
494 memcpy(&Command[8], CDB, CDB_Length);
497 Command = (unsigned char *)
498 malloc(8 + max(CDB_Length + DataBufferLength, RequestSenseLength));
499 memcpy(&Command[0], &DataBufferLength, 4);
500 memcpy(&Command[4], &Zero, 4);
501 memcpy(&Command[8], CDB, CDB_Length);
502 memcpy(&Command[8 + CDB_Length], DataBuffer, DataBufferLength);
506 DecodeSCSI(CDB, "SCSI_ExecuteCommand : ");
508 Result = ioctl(pDev[DeviceFD].fd, SCSI_IOCTL_SEND_COMMAND, Command);
510 memcpy(pRequestSense, &Command[8], RequestSenseLength);
511 else if (Direction == Input)
512 memcpy(DataBuffer, &Command[8], DataBufferLength);
514 SCSI_CloseDevice(DeviceFD);
529 * Send the command to the device with the
532 int Tape_Ioctl( int DeviceFD, int command)
534 extern OpenFiles_T *pDev;
538 if (pDev[DeviceFD].devopen == 0)
540 SCSI_OpenDevice(DeviceFD);
553 if (ioctl(pDev[DeviceFD].fd , MTIOCTOP, &mtop) != 0)
555 dbprintf(("Tape_Ioctl error ioctl %d\n",errno));
556 SCSI_CloseDevice(DeviceFD);
560 SCSI_CloseDevice(DeviceFD);
564 int Tape_Status( int DeviceFD)
566 extern OpenFiles_T *pDev;
570 if (pDev[DeviceFD].devopen == 0)
572 SCSI_OpenDevice(DeviceFD);
575 if (ioctl(pDev[DeviceFD].fd , MTIOCGET, &mtget) != 0)
577 DebugPrint(DEBUG_ERROR, SECTION_TAPE,"Tape_Status error ioctl %d\n",errno);
578 SCSI_CloseDevice(DeviceFD);
582 DebugPrint(DEBUG_INFO, SECTION_TAPE,"ioctl -> mtget.mt_gstat %lX\n",mtget.mt_gstat);
583 if (GMT_ONLINE(mtget.mt_gstat))
588 if (GMT_BOT(mtget.mt_gstat))
590 ret = ret | TAPE_BOT;
593 if (GMT_EOT(mtget.mt_gstat))
595 ret = ret | TAPE_EOT;
598 if (GMT_WR_PROT(mtget.mt_gstat))
600 ret = ret | TAPE_WR_PROT;
603 if (GMT_DR_OPEN(mtget.mt_gstat))
605 ret = ret | TAPE_NOT_LOADED;
608 SCSI_CloseDevice(DeviceFD);
613 * This functions scan all /dev/sg* devices
614 * It opens the device an print the result of the inquiry
617 int ScanBus(int print)
620 struct dirent *dirent;
621 extern OpenFiles_T *pDev;
625 dir = opendir("/dev/");
627 while ((dirent = readdir(dir)) != NULL)
629 if (strstr(dirent->d_name, "sg") != NULL)
631 pDev[count].dev = malloc(10);
632 pDev[count].inqdone = 0;
633 sprintf(pDev[count].dev,"/dev/%s", dirent->d_name);
634 if (OpenDevice(count,pDev[count].dev, "Scan", NULL ))
636 SCSI_CloseDevice(count);
637 pDev[count].inqdone = 0;
641 printf("name /dev/%s ", dirent->d_name);
643 switch (pDev[count].inquiry->type)
676 printf("unknown %d",pDev[count].inquiry->type);
682 printf("Count %d\n",count);
684 free(pDev[count].dev);
685 pDev[count].dev=NULL;
694 * indent-tabs-mode: nil