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 2006/07/06 11:57:28 martinea Exp $
29 * Interface to execute SCSI commands on Linux
31 * Copyright (c) Thomas Hepper th@ant.han.de
42 #ifdef HAVE_SYS_MTIO_H
46 #include <scsi-defs.h>
48 extern OpenFiles_T *pDev;
50 void SCSI_OS_Version(void)
53 static char rcsid[] = "$Id: scsi-linux.c,v 1.30 2006/07/06 11:57:28 martinea Exp $";
54 DebugPrint(DEBUG_ERROR, SECTION_INFO, "scsi-os-layer: %s\n",rcsid);
58 int SCSI_CloseDevice(int DeviceFD)
62 if (pDev[DeviceFD].devopen == 1)
64 pDev[DeviceFD].devopen = 0;
65 ret = close(pDev[DeviceFD].fd);
71 /* Open a device to talk to an scsi device, either per ioctl, or
78 * Define some readable defs for the falgs which can be set (like in the AIX dreiver)
82 int SCSI_OpenDevice(int ip)
88 char *buffer = NULL ; /* Will contain the device name after checking */
89 int openmode = O_RDONLY;
91 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### START SCSI_OpenDevice\n"));
92 if (pDev[ip].inqdone == 0)
95 if (strncmp("/dev/sg", pDev[ip].dev, 7) != 0) /* Check if no sg device for an link .... */
97 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("SCSI_OpenDevice : checking if %s is a sg device\n"), pDev[ip].dev);
98 if (lstat(pDev[ip].dev, &pstat) != -1)
100 if (S_ISLNK(pstat.st_mode) == 1)
102 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("SCSI_OpenDevice : is a link, checking destination\n"));
103 if ((buffer = (char *)malloc(513)) == NULL)
105 DebugPrint(DEBUG_ERROR, SECTION_SCSI,_("SCSI_OpenDevice : malloc failed\n"));
108 memset(buffer, 0, 513);
109 if (( i = readlink(pDev[ip].dev, buffer, 512)) == -1)
111 if (errno == ENAMETOOLONG )
119 if (strncmp("/dev/sg", buffer, 7) == 0)
121 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("SCSI_OpenDevice : link points to %s\n"), buffer) ;
125 } else {/* S_ISLNK(pstat.st_mode) == 1 */
126 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("No link %s\n"), pDev[ip].dev) ;
127 buffer = stralloc(pDev[ip].dev);
129 } else {/* lstat(DeviceName, &pstat) != -1 */
130 DebugPrint(DEBUG_ERROR, SECTION_SCSI,_("can't stat device %s\n"), pDev[ip].dev);
134 buffer = stralloc(pDev[ip].dev);
138 if (pDev[ip].flags == 1)
143 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("Try to open %s\n"), buffer);
144 if ((DeviceFD = open(buffer, openmode)) >= 0)
147 pDev[ip].devopen = 1;
148 pDev[ip].fd = DeviceFD;
150 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### STOP SCSI_OpenDevice open failed\n"));
155 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("done\n"));
156 if ( pDev[ip].flags == 1)
161 pDev[ip].dev = buffer;
162 if (pDev[ip].SCSI == 1)
164 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("SCSI_OpenDevice : use SG interface\n"));
165 if ((timeout = ioctl(pDev[ip].fd, SG_GET_TIMEOUT)) > 0)
167 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("SCSI_OpenDevice : current timeout %d\n"), timeout);
169 if (ioctl(pDev[ip].fd, SG_SET_TIMEOUT, &timeout) == 0)
171 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("SCSI_OpenDevice : timeout set to %d\n"), timeout);
174 pDev[ip].inquiry = (SCSIInquiry_T *)malloc(INQUIRY_SIZE);
175 if (SCSI_Inquiry(ip, pDev[ip].inquiry, (u_char)INQUIRY_SIZE) == 0)
177 if (pDev[ip].inquiry->type == TYPE_TAPE || pDev[ip].inquiry->type == TYPE_CHANGER)
180 pDev[ip].ident[i] = pDev[ip].inquiry->prod_ident[i];
181 for (i=15; i >= 0 && !isalnum(pDev[ip].ident[i]); i--)
183 pDev[ip].ident[i] = '\0';
187 if (pDev[ip].inquiry->type == TYPE_TAPE)
189 pDev[ip].type = stralloc("tape");
192 if (pDev[ip].inquiry->type == TYPE_CHANGER)
194 pDev[ip].type = stralloc("changer");
197 PrintInquiry(pDev[ip].inquiry);
198 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### STOP SCSI_OpenDevice (1)\n"));
202 amfree(pDev[ip].inquiry);
203 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### STOP SCSI_OpenDevice (0)\n"));
208 pDev[ip].devopen = 0;
210 amfree(pDev[ip].inquiry);
211 pDev[ip].inquiry = NULL;
212 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### STOP SCSI_OpenDevice (1)\n"));
215 } else /* if (pDev[ip].SCSI == 1) */ {
216 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("Device not capable for SCSI commands\n"));
218 pDev[ip].devopen = 0;
220 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### STOP SCSI_OpenDevice (1)\n"));
223 } else { /* if (pDev[ip].inqdone == 0) */
224 if (pDev[ip].flags == 1)
230 if ((DeviceFD = open(pDev[ip].dev, openmode)) >= 0)
232 pDev[ip].devopen = 1;
233 pDev[ip].fd = DeviceFD;
234 if (pDev[ip].flags == 1)
236 if ((timeout = ioctl(pDev[ip].fd, SG_GET_TIMEOUT)) > 0)
238 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("SCSI_OpenDevice : current timeout %d\n"), timeout);
240 if (ioctl(pDev[ip].fd, SG_SET_TIMEOUT, &timeout) == 0)
242 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("SCSI_OpenDevice : timeout set to %d\n"), timeout);
246 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### STOP SCSI_OpenDevice (1)\n"));
249 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### STOP SCSI_OpenDevice open failed\n"));
253 DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### STOP SCSI_OpenDevice should not happen !!\n"));
257 #define SCSI_OFF SIZEOF(struct sg_header)
258 int SCSI_ExecuteCommand(int DeviceFD,
259 Direction_T Direction,
263 size_t DataBufferLength,
264 RequestSense_T *pRequestSense,
265 size_t RequestSenseLength)
267 struct sg_header *psg_header;
272 /* Basic sanity checks */
273 assert(CDB_Length <= UCHAR_MAX);
274 assert(RequestSenseLength <= UCHAR_MAX);
276 /* Clear buffer for cases where sense is not returned */
277 memset(pRequestSense, 0, RequestSenseLength);
279 if (pDev[DeviceFD].avail == 0)
284 if (pDev[DeviceFD].devopen == 0)
285 if (SCSI_OpenDevice(DeviceFD) == 0)
288 if (SCSI_OFF + CDB_Length + DataBufferLength > 4096)
290 SCSI_CloseDevice(DeviceFD);
294 buffer = (char *)malloc(SCSI_OFF + CDB_Length + DataBufferLength);
297 dbprintf(_("SCSI_ExecuteCommand memory allocation failure.\n"));
298 SCSI_CloseDevice(DeviceFD);
301 memset(buffer, 0, SCSI_OFF + CDB_Length + DataBufferLength);
302 memcpy(buffer + SCSI_OFF, CDB, CDB_Length);
304 psg_header = (struct sg_header *)buffer;
305 if (CDB_Length >= 12)
307 psg_header->twelve_byte = 1;
309 psg_header->twelve_byte = 0;
311 psg_header->result = 0;
312 psg_header->reply_len = (int)(SCSI_OFF + DataBufferLength);
320 osize = DataBufferLength;
324 DecodeSCSI(CDB, "SCSI_ExecuteCommand : ");
326 status = write(pDev[DeviceFD].fd, buffer, SCSI_OFF + CDB_Length + osize);
327 if ( (status < (ssize_t)0) ||
328 (status != (ssize_t)(SCSI_OFF + CDB_Length + osize)) ||
329 (psg_header->result != 0))
331 dbprintf(_("SCSI_ExecuteCommand error send \n"));
332 SCSI_CloseDevice(DeviceFD);
337 memset(buffer, 0, SCSI_OFF + DataBufferLength);
338 status = read(pDev[DeviceFD].fd, buffer, SCSI_OFF + DataBufferLength);
339 memset(pRequestSense, 0, RequestSenseLength);
340 memcpy(pRequestSense, psg_header->sense_buffer, 16);
343 (status != (ssize_t)(SCSI_OFF + DataBufferLength)) ||
344 (psg_header->result != 0))
346 dbprintf(_("SCSI_ExecuteCommand error read \n"));
347 dbprintf(_("Status %zd (%zd) %2X\n"), status, SCSI_OFF + DataBufferLength,psg_header->result );
348 SCSI_CloseDevice(DeviceFD);
353 if (DataBufferLength)
355 memcpy(DataBuffer, buffer + SCSI_OFF, DataBufferLength);
358 SCSI_CloseDevice(DeviceFD);
365 static inline int min(int x, int y)
367 return (x < y ? x : y);
371 static inline int max(int x, int y)
373 return (x > y ? x : y);
376 int SCSI_OpenDevice(int ip)
381 if (pDev[ip].inqdone == 0)
383 pDev[ip].inqdone = 1;
384 if ((DeviceFD = open(pDev[ip].dev, O_RDWR)) >= 0)
387 pDev[ip].fd = DeviceFD;
389 pDev[ip].inquiry = (SCSIInquiry_T *)malloc(INQUIRY_SIZE);
390 dbprintf(_("SCSI_OpenDevice : use ioctl interface\n"));
391 if (SCSI_Inquiry(ip, pDev[ip].inquiry, (u_char)INQUIRY_SIZE) == 0)
393 if (pDev[ip].inquiry->type == TYPE_TAPE || pDev[ip].inquiry->type == TYPE_CHANGER)
395 for (i=0;i < 16 && pDev[ip].inquiry->prod_ident[i] != ' ';i++)
396 pDev[ip].ident[i] = pDev[ip].inquiry->prod_ident[i];
397 pDev[ip].ident[i] = '\0';
399 PrintInquiry(pDev[ip].inquiry);
402 amfree(pDev[ip].inquiry);
408 amfree(pDev[ip].inquiry);
409 pDev[ip].inquiry = NULL;
415 if ((DeviceFD = open(pDev[ip].dev, O_RDWR)) >= 0)
417 pDev[ip].fd = DeviceFD;
418 pDev[ip].devopen = 1;
421 pDev[ip].devopen = 0;
427 int SCSI_ExecuteCommand(int DeviceFD,
428 Direction_T Direction,
432 int DataBufferLength,
433 RequestSense_T *pRequestSense,
434 int RequestSenseLength)
436 unsigned char *Command;
437 int Zero = 0, Result;
439 if (pDev[DeviceFD].avail == 0)
444 if (pDev[DeviceFD].devopen == 0)
446 if (SCSI_OpenDevice(DeviceFD) == 0)
450 memset(pRequestSense, 0, RequestSenseLength);
454 Command = (unsigned char *)
455 malloc(8 + max(DataBufferLength, RequestSenseLength));
456 memcpy(&Command[0], &Zero, 4);
457 memcpy(&Command[4], &DataBufferLength, 4);
458 memcpy(&Command[8], CDB, CDB_Length);
461 Command = (unsigned char *)
462 malloc(8 + max(CDB_Length + DataBufferLength, RequestSenseLength));
463 memcpy(&Command[0], &DataBufferLength, 4);
464 memcpy(&Command[4], &Zero, 4);
465 memcpy(&Command[8], CDB, CDB_Length);
466 memcpy(&Command[8 + CDB_Length], DataBuffer, DataBufferLength);
470 DecodeSCSI(CDB, "SCSI_ExecuteCommand : ");
472 Result = ioctl(pDev[DeviceFD].fd, SCSI_IOCTL_SEND_COMMAND, Command);
474 memcpy(pRequestSense, &Command[8], RequestSenseLength);
475 else if (Direction == Input)
476 memcpy(DataBuffer, &Command[8], DataBufferLength);
478 SCSI_CloseDevice(DeviceFD);
493 * Send the command to the device with the
496 int Tape_Ioctl( int DeviceFD, int command)
501 if (pDev[DeviceFD].devopen == 0)
503 if (SCSI_OpenDevice(DeviceFD) == 0)
517 if (ioctl(pDev[DeviceFD].fd , MTIOCTOP, &mtop) != 0)
519 dbprintf(_("Tape_Ioctl error ioctl %s\n"),strerror(errno));
520 SCSI_CloseDevice(DeviceFD);
524 SCSI_CloseDevice(DeviceFD);
528 int Tape_Status( int DeviceFD)
533 memset(&mtget, 0, SIZEOF(mtget));
534 if (pDev[DeviceFD].devopen == 0)
536 if (SCSI_OpenDevice(DeviceFD) == 0)
540 if (ioctl(pDev[DeviceFD].fd , MTIOCGET, &mtget) != 0)
542 DebugPrint(DEBUG_ERROR, SECTION_TAPE,_("Tape_Status error ioctl %s\n"),
544 SCSI_CloseDevice(DeviceFD);
548 DebugPrint(DEBUG_INFO, SECTION_TAPE,_("ioctl -> mtget.mt_gstat %lX\n"),mtget.mt_gstat);
549 if (GMT_ONLINE(mtget.mt_gstat))
554 if (GMT_BOT(mtget.mt_gstat))
556 ret = ret | TAPE_BOT;
559 if (GMT_EOT(mtget.mt_gstat))
561 ret = ret | TAPE_EOT;
564 if (GMT_WR_PROT(mtget.mt_gstat))
566 ret = ret | TAPE_WR_PROT;
569 if (GMT_DR_OPEN(mtget.mt_gstat))
571 ret = ret | TAPE_NOT_LOADED;
574 SCSI_CloseDevice(DeviceFD);
579 * This functions scan all /dev/sg* devices
580 * It opens the device an print the result of the inquiry
583 int ScanBus(int print)
586 struct dirent *dirent;
589 if ((dir = opendir("/dev/")) == NULL)
591 dbprintf(_("/dev/ error: %s"), strerror(errno));
595 while ((dirent = readdir(dir)) != NULL)
597 if (strstr(dirent->d_name, "sg") != NULL)
599 pDev[count].dev = malloc(10);
600 pDev[count].inqdone = 0;
601 g_snprintf(pDev[count].dev, SIZEOF(pDev[count].dev),
602 "/dev/%s", dirent->d_name);
603 if (OpenDevice(count,pDev[count].dev, "Scan", NULL ))
605 SCSI_CloseDevice(count);
606 pDev[count].inqdone = 0;
610 g_printf(_("name /dev/%s "), dirent->d_name);
612 switch (pDev[count].inquiry->type)
621 g_printf(_("Printer"));
624 g_printf(_("Processor"));
630 g_printf(_("Cdrom"));
633 g_printf(_("Scanner"));
636 g_printf(_("Optical"));
639 g_printf(_("Changer"));
645 g_printf(_("unknown %d"),pDev[count].inquiry->type);
651 g_printf(_("Count %d\n"),count);
653 amfree(pDev[count].dev);
654 pDev[count].dev=NULL;
662 * indent-tabs-mode: nil