/* MTX -- SCSI Tape Attached Medium Changer Control Program
- Copyright 1997-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
+ Copyright 1997-1998 by Leonard N. Zubkoff.
+ Copyright 1999-2006 by Eric Lee Green.
+ Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
-$Date: 2002/02/05 16:51:11 $
-$Revision: 1.8.2.3 $
+ $Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
+ $Revision: 193 $
- This file created Feb 2000 by Eric Lee Green <eric@badtux.org> from pieces
- extracted from mtx.c, plus some additional routines.
+ This file created Feb 2000 by Eric Lee Green <eric@badtux.org> from pieces
+ extracted from mtx.c, plus a near total re-write of most of the beast.
- This program is free software; you may redistribute and/or modify it under
- the terms of the GNU General Public License Version 2 as published by the
- Free Software Foundation.
+ This program is free software; you may redistribute and/or modify it under
+ the terms of the GNU General Public License Version 2 as published by the
+ Free Software Foundation.
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for complete details.
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for complete details.
*/
-/* FatalError: changed Feb. 2000 elg@estinc.com to eliminate a buffer
- overflow :-(. That could be important if mtxl is SUID for some reason.
+/*
+ * FatalError: changed Feb. 2000 elg@badtux.org to eliminate a buffer
+ * overflow :-(. That could be important if mtxl is SUID for some reason.
*/
#include "mtx.h"
#include "mtxl.h"
+/* #define DEBUG_NSM 1 */
+
/* #define DEBUG_MODE_SENSE 1 */
/* #define DEBUG */
/* #define DEBUG_SCSI */
+#define __WEIRD_CHAR_SUPPRESS 1
/* zap the following define when we finally add real import/export support */
#define IMPORT_EXPORT_HACK 1 /* for the moment, import/export == storage */
/* the camlib is used on FreeBSD. */
#if HAVE_CAMLIB_H
-# include "scsi_freebsd.c"
+# include "scsi_freebsd.c"
#endif
/* the scsi_ctl interface is used on HP/UX. */
#if HAVE_SYS_SCSI_CTL_H
-# include "scsi_hpux.c"
+# include "scsi_hpux.c"
#endif
/* the 'sg' interface is used on Linux. */
#if HAVE_SCSI_SG_H
-# include "scsi_linux.c"
+# include "scsi_linux.c"
+#endif
+
+/* the IOCTL_SCSI_PASSTHROUGH interface is used on Windows. */
+#if HAVE_DDK_NTDDSCSI_H || defined(_MSC_VER)
+# include "scsi_win32.c"
#endif
/* The 'uscsi' interface is used on Solaris. */
#if HAVE_SYS_SCSI_IMPL_USCSI_H
-# include "scsi_sun.c"
+# include "scsi_sun.c"
#endif
-/* The 'tm_buf' interface, as used on AIX. */
-#ifdef HAVE_SYS_SCSI_H
-# include "scsi_aix.c"
+/* The 'gsc' interface, is used on AIX. */
+#if HAVE_SYS_GSCDDS_H
+# include "scsi_aix.c"
#endif
/* The 'dslib' interface is used on SGI. */
#if HAVE_DSLIB_H
-# include "scsi_sgi.c"
+# include "scsi_sgi.c"
#endif
/* Hmm, dunno what to do about Digital Unix at the moment. */
#ifdef DIGITAL_UNIX
-#include "du/scsi.c"
+# include "du/scsi.c"
#endif
-
#ifdef VMS
-#include "[.vms]scsi.c"
+# include "[.vms]scsi.c"
#endif
extern char *argv0; /* something to let us do good error messages. */
+/* create a global RequestSenseT value. */
+RequestSense_T scsi_error_sense;
+
/* Now for some low-level SCSI stuff: */
-Inquiry_T *RequestInquiry(DEVICE_TYPE fd, RequestSense_T *RequestSense) {
- Inquiry_T *Inquiry;
- CDB_T CDB;
-
- Inquiry = (Inquiry_T *) xmalloc(sizeof(Inquiry_T));
-
- CDB[0] = 0x12; /* INQUIRY */
- CDB[1] = 0; /* EVPD = 0 */
- CDB[2] = 0; /* Page Code */
- CDB[3] = 0; /* Reserved */
- CDB[4] = sizeof(Inquiry_T); /* Allocation Length */
- CDB[5] = 0; /* Control */
-
- /* set us a very short timeout, sigh... */
- SCSI_Set_Timeout(30); /* 30 seconds, sigh! */
-
- if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,
- Inquiry, sizeof(Inquiry_T), RequestSense) != 0)
- {
- free(Inquiry);
- return NULL; /* sorry! */
- }
- return Inquiry;
+Inquiry_T *RequestInquiry(DEVICE_TYPE fd, RequestSense_T *RequestSense)
+{
+ Inquiry_T *Inquiry;
+ CDB_T CDB;
+
+ Inquiry = (Inquiry_T *) xmalloc(sizeof(Inquiry_T));
+
+ CDB[0] = 0x12; /* INQUIRY */
+ CDB[1] = 0; /* EVPD = 0 */
+ CDB[2] = 0; /* Page Code */
+ CDB[3] = 0; /* Reserved */
+ CDB[4] = sizeof(Inquiry_T); /* Allocation Length */
+ CDB[5] = 0; /* Control */
+
+ /* set us a very short timeout, sigh... */
+ SCSI_Set_Timeout(30); /* 30 seconds, sigh! */
+
+ if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, Inquiry, sizeof(Inquiry_T), RequestSense) != 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "SCSI Inquiry Command failed\n");
+#endif
+ free(Inquiry);
+ return NULL; /* sorry! */
+ }
+ return Inquiry;
}
-int BigEndian16(unsigned char *BigEndianData)
+#if defined(DEBUG_NSM) || defined(DEBUG_EXCHANGE)
+/* DEBUG */
+static void dump_cdb(unsigned char *CDB, int len)
{
- return (BigEndianData[0] << 8) + BigEndianData[1];
+ fprintf(stderr,"CDB:");
+ PrintHex(1, CDB, len);
}
+#endif
-int BigEndian24(unsigned char *BigEndianData)
+#if defined(DEBUG_NSM) || defined(DEBUG_ADIC)
+/* DEBUG */
+static void dump_data(unsigned char *data, int len)
{
- return (BigEndianData[0] << 16) + (BigEndianData[1] << 8) + BigEndianData[2];
+ if (!len)
+ {
+ fprintf(stderr,"**NO DATA**\n");
+ return;
+ }
+
+ fprintf(stderr,"DATA:");
+ PrintHex(1, data, len);
}
+#endif
+int BigEndian16(unsigned char *BigEndianData)
+{
+ return (BigEndianData[0] << 8) + BigEndianData[1];
+}
+int BigEndian24(unsigned char *BigEndianData)
+{
+ return (BigEndianData[0] << 16) + (BigEndianData[1] << 8) + BigEndianData[2];
+}
+
void FatalError(char *ErrorMessage, ...)
{
#define FORMAT_BUF_LEN 1024
- char FormatBuffer[FORMAT_BUF_LEN];
- char *SourcePointer;
- char *TargetPointer = FormatBuffer;
- int Character, LastCharacter = '\0';
- int numchars = 0;
-
- va_list ArgumentPointer;
- va_start(ArgumentPointer, ErrorMessage);
- /* SourcePointer = "mtx: "; */
- sprintf(TargetPointer,"%s: ",argv0);
- numchars=strlen(TargetPointer);
-
- /* while ((Character = *SourcePointer++) != '\0') { */
- /* *TargetPointer++ = Character; */
- /* numchars++; */
- /* } */
-
-
- while ((Character = *ErrorMessage++) != '\0')
- if (LastCharacter == '%')
- {
- if (Character == 'm')
- {
- SourcePointer = strerror(errno);
- while ((Character = *SourcePointer++) != '\0') {
- *TargetPointer++ = Character;
- numchars++;
- if (numchars==FORMAT_BUF_LEN-1) break;
- }
- if (numchars==FORMAT_BUF_LEN-1) break; /* break outer loop... */
- }
- else
- {
- *TargetPointer++ = '%';
- *TargetPointer++ = Character;
- numchars++;
- if (numchars==FORMAT_BUF_LEN-1) break;
- }
- LastCharacter = '\0';
- }
- else
- {
- if (Character != '%') {
- *TargetPointer++ = Character;
- numchars++;
- if (numchars==FORMAT_BUF_LEN-1) break;
+
+ char FormatBuffer[FORMAT_BUF_LEN];
+ char *SourcePointer;
+ char *TargetPointer = FormatBuffer;
+ char Character, LastCharacter = '\0';
+ int numchars = 0;
+
+ va_list ArgumentPointer;
+ va_start(ArgumentPointer, ErrorMessage);
+ /* SourcePointer = "mtx: "; */
+ sprintf(TargetPointer,"%s: ",argv0);
+ numchars=strlen(TargetPointer);
+
+ while ((Character = *ErrorMessage++) != '\0')
+ {
+ if (LastCharacter == '%')
+ {
+ if (Character == 'm')
+ {
+ SourcePointer = strerror(errno);
+ while ((Character = *SourcePointer++) != '\0')
+ {
+ *TargetPointer++ = Character;
+ numchars++;
+ if (numchars == FORMAT_BUF_LEN - 1)
+ {
+ break;
+ }
+ }
+ if (numchars == FORMAT_BUF_LEN - 1)
+ {
+ break; /* break outer loop... */
+ }
+ }
+ else
+ {
+ *TargetPointer++ = '%';
+ *TargetPointer++ = Character;
+ numchars++;
+ if (numchars == FORMAT_BUF_LEN - 1)
+ {
+ break;
+ }
+ }
+ LastCharacter = '\0';
+ }
+ else
+ {
+ if (Character != '%')
+ {
+ *TargetPointer++ = Character;
+ numchars++;
+ if (numchars == FORMAT_BUF_LEN-1)
+ {
+ break;
+ }
+ }
+ LastCharacter = Character;
+ }
}
- LastCharacter = Character;
- }
- *TargetPointer = '\0'; /* works even if we had to break from above... */
- vfprintf(stderr, FormatBuffer, ArgumentPointer);
- va_end(ArgumentPointer);
+
+ *TargetPointer = '\0'; /* works even if we had to break from above... */
+ vfprintf(stderr, FormatBuffer, ArgumentPointer);
+ va_end(ArgumentPointer);
+
#ifndef VMS
- exit(1);
+ exit(1);
#else
- sys$exit(VMS_ExitCode);
+ sys$exit(VMS_ExitCode);
#endif
}
/* This is a really slow and stupid 'bzero' implementation'... */
-void slow_bzero(char *buffer, int numchars) {
- while (numchars--) *buffer++ = 0;
+void slow_bzero(char *buffer, int numchars)
+{
+ while (numchars--)
+ {
+ *buffer++ = 0;
+ }
}
-
/* malloc some memory while checking for out-of-memory conditions. */
void *xmalloc(size_t Size)
{
- void *Result = (void *) malloc(Size);
- if (Result == NULL)
- FatalError("cannot allocate %d bytes of memory\n", Size);
- return Result;
+ void *Result = (void *) malloc(Size);
+ if (Result == NULL)
+ {
+ FatalError("cannot allocate %d bytes of memory\n", Size);
+ }
+ return Result;
}
/* malloc some memory, zeroing it too: */
void *xzmalloc(size_t Size)
{
- void *Result=(void *)xmalloc(Size);
-
- slow_bzero(Result,Size);
- return Result;
+ void *Result = (void *)xmalloc(Size);
+
+ slow_bzero(Result, Size);
+ return Result;
}
int min(int x, int y)
{
- return (x < y ? x : y);
+ return (x < y ? x : y);
}
int max(int x, int y)
{
- return (x > y ? x : y);
+ return (x > y ? x : y);
+}
+
+
+/* Okay, this is a hack for the NSM modular jukebox series, which
+ * uses the "SEND DIAGNOSTIC" command to do shit.
+ */
+
+int SendNSMHack(DEVICE_TYPE MediumChangerFD, NSM_Param_T *nsm_command,
+ int param_len, int timeout)
+{
+ CDB_T CDB;
+ int list_len = param_len + sizeof(NSM_Param_T) - 2048;
+
+ /* Okay, now for the command: */
+ CDB[0] = 0x1D;
+ CDB[1] = 0x10;
+ CDB[2] = 0;
+ CDB[3] = (unsigned char)(list_len >> 8);
+ CDB[4] = (unsigned char)list_len;
+ CDB[5] = 0;
+
+#ifdef DEBUG_NSM
+ dump_cdb(&CDB,6);
+ dump_data(nsm_command,list_len);
+#endif
+ fflush(stderr);
+ /* Don't set us any timeout unless timeout is > 0 */
+ if (timeout > 0)
+ {
+ SCSI_Set_Timeout(timeout); /* 30 minutes, sigh! */
+ }
+
+ /* Now send the command: */
+ if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6, nsm_command, list_len, &scsi_error_sense))
+ {
+ return -1; /* we failed */
+ }
+ return 0; /* Did it! */
}
+NSM_Result_T *RecNSMHack( DEVICE_TYPE MediumChangerFD,
+ int param_len, int timeout)
+{
+ CDB_T CDB;
+
+ NSM_Result_T *retval = (NSM_Result_T *)xzmalloc(sizeof(NSM_Result_T));
+
+ int list_len = param_len + sizeof(NSM_Result_T) - 0xffff;
+
+ /* Okay, now for the command: */
+ CDB[0] = 0x1C;
+ CDB[1] = 0x00;
+ CDB[2] = 0;
+ CDB[3] = (unsigned char)(list_len >> 8);
+ CDB[4] = (unsigned char)list_len;
+ CDB[5] = 0;
+
+#ifdef DEBUG_NSM
+ dump_cdb(&CDB,6);
+#endif
+
+ /* Don't set us any timeout unless timeout is > 0 */
+ if (timeout > 0)
+ {
+ SCSI_Set_Timeout(timeout);
+ }
+
+ /* Now send the command: */
+ if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6, retval, list_len, &scsi_error_sense))
+ {
+ return NULL; /* we failed */
+ }
+
+#ifdef DEBUG_NSM
+ fprintf(stderr,
+ "page_code=%02x page_len=%d command_code=%s\n",
+ retval->page_code,
+ (int) ((retval->page_len_msb << 8) + retval->page_len_lsb),
+ retval->command_code);
+#endif
+
+ return retval; /* Did it! (maybe!)*/
+}
/* Routine to inventory the library. Needed by, e.g., some Breece Hill
* loaders. Sends an INITIALIZE_ELEMENT_STATUS command. This command
* has no parameters, such as a range to scan :-(.
*/
-int Inventory(DEVICE_TYPE MediumChangerFD) {
- CDB_T CDB;
- RequestSense_T RequestSense;
-
- /* okay, now for the command: */
- CDB[0]=0x07;
- CDB[1]=CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
-
- /* set us a very long timeout, sigh... */
- SCSI_Set_Timeout(30*60); /* 30 minutes, sigh! */
-
- if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,NULL,0,&RequestSense) != 0) {
- return -1; /* could not do! */
- }
- return 0; /* did do! */
-}
+int Inventory(DEVICE_TYPE MediumChangerFD)
+{
+ CDB_T CDB;
-/* Routine to send an UNLOAD from the SSC spec to a device. This can be
- * used either to eject a tape (when sent to a tape drive), or to eject
- * a magzine (on some Seagate changers, when sent to LUN 1 ).
- */
+ /* okay, now for the command: */
+ CDB[0] = 0x07;
+ CDB[1] = CDB[2] = CDB[3] = CDB[4] = CDB[5] = 0;
-int Eject(DEVICE_TYPE fd) {
- CDB_T CDB;
- RequestSense_T RequestSense;
- /* okay, now for the command: */
-
- CDB[0]=0x1b;
- CDB[1]=CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
-
- if (SCSI_ExecuteCommand(fd,Input,&CDB,6,NULL,0,&RequestSense) != 0) {
- return -1; /* could not do! */
- }
- return 0; /* did do! */
-}
+ /* set us a very long timeout, sigh... */
+ SCSI_Set_Timeout(30 * 60); /* 30 minutes, sigh! */
+
+ if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,NULL,0,&scsi_error_sense) != 0)
+ {
+ /* If error is UNIT ATTENTION then retry the request */
+ if (scsi_error_sense.ErrorCode != 0x70 || scsi_error_sense.SenseKey != 6 ||
+ ClearUnitAttention(MediumChangerFD, &scsi_error_sense) != 0 ||
+ SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,NULL,0,&scsi_error_sense) != 0)
+ {
+ PrintRequestSense(&scsi_error_sense);
+ fprintf(stderr, "Initialize Element Status (0x07) failed\n");
+ return -1; /* could not do! */
+ }
+ }
+ return 0; /* did do! */
+}
/* Routine to read the Mode Sense Element Address Assignment Page */
/* We try to read the page. If we can't read the page, we return NULL.
* Our caller really isn't too worried about why we could not read the
* page, it will simply default to some kind of default values.
*/
-ElementModeSense_T *ReadAssignmentPage(DEVICE_TYPE MediumChangerFD) {
- CDB_T CDB;
- ElementModeSense_T *retval; /* processed SCSI. */
- unsigned char input_buffer[136];
- ElementModeSensePage_T *sense_page; /* raw SCSI. */
- RequestSense_T RequestSense; /* see what retval of raw SCSI was... */
-
- /* okay, now for the command: */
- CDB[0]=0x1a; /* Mode Sense(6) */
- CDB[1]=0;
- CDB[2]=0x1d; /* Mode Sense Element Address Assignment Page */
- CDB[3]=0;
- CDB[4]=136; /* allocation_length... */
- CDB[5]=0;
-
- /* clear the data buffer: */
- slow_bzero((char *)&RequestSense,sizeof(RequestSense_T));
- slow_bzero((char *)input_buffer,sizeof(input_buffer));
-
- if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,
- &input_buffer,sizeof(input_buffer),&RequestSense) != 0) {
+ElementModeSense_T *ReadAssignmentPage(DEVICE_TYPE MediumChangerFD)
+{
+ CDB_T CDB;
+ ElementModeSense_T *retval; /* processed SCSI. */
+ unsigned char input_buffer[136];
+ ElementModeSensePage_T *sense_page; /* raw SCSI. */
+
+ /* okay, now for the command: */
+ CDB[0] = 0x1A; /* Mode Sense(6) */
+ CDB[1] = 0x08;
+ CDB[2] = 0x1D; /* Mode Sense Element Address Assignment Page */
+ CDB[3] = 0;
+ CDB[4] = 136; /* allocation_length... */
+ CDB[5] = 0;
+
+ /* clear the data buffer: */
+ slow_bzero((char *)&scsi_error_sense, sizeof(RequestSense_T));
+ slow_bzero((char *)input_buffer, sizeof(input_buffer));
+
+ if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6,
+ &input_buffer, sizeof(input_buffer), &scsi_error_sense) != 0)
+ {
+ /* If error is UNIT ATTENTION then retry the request */
+ if (scsi_error_sense.ErrorCode != 0x70 || scsi_error_sense.SenseKey != 6 ||
+ ClearUnitAttention(MediumChangerFD, &scsi_error_sense) != 0 ||
+ SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6,
+ &input_buffer, sizeof(input_buffer), &scsi_error_sense) != 0)
+ {
+ PrintRequestSense(&scsi_error_sense);
+ fprintf(stderr,"Mode sense (0x1A) for Page 0x1D failed\n");
+ fflush(stderr);
+ return NULL; /* sorry, couldn't do it. */
+ }
+ }
+
+ /* Could do it, now build return value: */
+
#ifdef DEBUG_MODE_SENSE
- fprintf(stderr,"Could not execute mode sense...\n");
- fflush(stderr);
+ PrintHex(0, input_buffer, 30);
#endif
- return NULL; /* sorry, couldn't do it. */
- }
- /* Could do it, now build return value: */
+ /* first, skip past: mode data header, and block descriptor header if any */
+ sense_page=(ElementModeSensePage_T *)(input_buffer+4+input_buffer[3]);
#ifdef DEBUG_MODE_SENSE
- {
- int i;
- for (i=0;i<30;i+=3) {
- fprintf(stderr,"ib[%d]=%d ib[%d]=%d ib[%d]=%d\n",
- i,input_buffer[i],i+1,input_buffer[i+1],
- i+2,input_buffer[i+2]);
- /* fprintf(stderr,"input_buffer[0]=%d input_buffer[3]=%d\n",
- * input_buffer[0], input_buffer[3]);
- */
- }
- fflush(stderr);
- }
+ fprintf(stderr,"*sense_page=%x %x\n",*((unsigned char *)sense_page),sense_page->PageCode);
+ fflush(stderr);
#endif
- /* first, skip past: mode data header, and block descriptor header if any */
- sense_page=(ElementModeSensePage_T *)(input_buffer+4+input_buffer[3]);
-#ifdef DEBUG_MODE_SENSE
- fprintf(stderr,"*sense_page=%x %x\n",*((unsigned char *)sense_page),sense_page->PageCode);
- fflush(stderr);
-#endif
+ retval = (ElementModeSense_T *)xzmalloc(sizeof(ElementModeSense_T));
- retval=(ElementModeSense_T *) xzmalloc(sizeof(ElementModeSense_T));
+ /* Remember that all SCSI values are big-endian: */
+ retval->MediumTransportStart =
+ (((int)sense_page->MediumTransportStartHi) << 8) +
+ sense_page->MediumTransportStartLo;
- /* Remember that all SCSI values are big-endian: */
- retval->MediumTransportStart=(((int)sense_page->MediumTransportStartHi)<<8) +
- sense_page->MediumTransportStartLo;
- retval->NumMediumTransport=(((int)(sense_page->NumMediumTransportHi))<<8) +
- sense_page->NumMediumTransportLo;
+ retval->NumMediumTransport =
+ (((int)(sense_page->NumMediumTransportHi))<<8) +
+ sense_page->NumMediumTransportLo;
- /* HACK! Some HP autochangers don't report NumMediumTransport right! */
- /* ARG! TAKE OUT THE %#@!# HACK! */
+ /* HACK! Some HP autochangers don't report NumMediumTransport right! */
+ /* ARG! TAKE OUT THE %#@!# HACK! */
#ifdef STUPID_DUMB_IDIOTIC_HP_LOADER_HACK
- if (!retval->NumMediumTransport) {
- retval->NumMediumTransport=1;
- }
+ if (!retval->NumMediumTransport)
+ {
+ retval->NumMediumTransport = 1;
+ }
#endif
#ifdef DEBUG_MODE_SENSE
- fprintf(stderr,"rawNumStorage= %d %d rawNumImportExport= %d %d\n",
- sense_page->NumStorageHi,sense_page->NumStorageLo,
- sense_page->NumImportExportHi, sense_page->NumImportExportLo);
- fprintf(stderr,"rawNumTransport=%d %d rawNumDataTransfer=%d %d\n",
- sense_page->NumMediumTransportHi,sense_page->NumMediumTransportLo,
- sense_page->NumDataTransferHi,sense_page->NumDataTransferLo);
- fflush(stderr);
+ fprintf(stderr, "rawNumStorage= %d %d rawNumImportExport= %d %d\n",
+ sense_page->NumStorageHi, sense_page->NumStorageLo,
+ sense_page->NumImportExportHi, sense_page->NumImportExportLo);
+ fprintf(stderr, "rawNumTransport=%d %d rawNumDataTransfer=%d %d\n",
+ sense_page->NumMediumTransportHi, sense_page->NumMediumTransportLo,
+ sense_page->NumDataTransferHi, sense_page->NumDataTransferLo);
+ fflush(stderr);
#endif
- retval->StorageStart=(((int)(sense_page->StorageStartHi))<<8) +
- sense_page->StorageStartLo;
- retval->NumStorage=(((int)(sense_page->NumStorageHi))<<8) +
- sense_page->NumStorageLo;
- retval->ImportExportStart=(((int)(sense_page->ImportExportStartHi))<<8)+
- sense_page->ImportExportStartLo;
- retval->NumImportExport=(((int)(sense_page->NumImportExportHi))<<8)+
- sense_page->NumImportExportLo;
- retval->DataTransferStart=(((int)(sense_page->DataTransferStartHi))<<8)+
- sense_page->DataTransferStartLo;
- retval->NumDataTransfer=(((int)(sense_page->NumDataTransferHi))<<8)+
- sense_page->NumDataTransferLo;
-
- /* allocate a couple spares 'cause some HP autochangers and maybe others
- * don't properly report the robotics arm(s) count here...
- */
- retval->NumElements=retval->NumStorage+retval->NumImportExport+
- retval->NumDataTransfer+retval->NumMediumTransport;
-
- retval->MaxReadElementStatusData=(sizeof(ElementStatusDataHeader_T) +
- 4 * sizeof(ElementStatusPage_T) +
- (retval->NumElements) *
- sizeof(TransportElementDescriptor_T));
+ retval->StorageStart =
+ ((int)sense_page->StorageStartHi << 8) + sense_page->StorageStartLo;
+
+ retval->NumStorage =
+ ((int)sense_page->NumStorageHi << 8) + sense_page->NumStorageLo;
+
+ retval->ImportExportStart =
+ ((int)sense_page->ImportExportStartHi << 8) + sense_page->ImportExportStartLo;
+
+ retval->NumImportExport =
+ ((int)sense_page->NumImportExportHi << 8) + sense_page->NumImportExportLo;
+
+ retval->DataTransferStart =
+ ((int)sense_page->DataTransferStartHi << 8) + sense_page->DataTransferStartLo;
+
+ retval->NumDataTransfer =
+ ((int)sense_page->NumDataTransferHi << 8) + sense_page->NumDataTransferLo;
+ /* allocate a couple spares 'cause some HP autochangers and maybe others
+ * don't properly report the robotics arm(s) count here...
+ */
+ retval->NumElements =
+ retval->NumStorage+retval->NumImportExport +
+ retval->NumDataTransfer+retval->NumMediumTransport;
+
+ retval->MaxReadElementStatusData =
+ (sizeof(ElementStatusDataHeader_T) +
+ 4 * sizeof(ElementStatusPage_T) +
+ retval->NumElements * sizeof(TransportElementDescriptor_T));
#ifdef IMPORT_EXPORT_HACK
- retval->NumStorage=retval->NumStorage+retval->NumImportExport;
+ retval->NumStorage = retval->NumStorage+retval->NumImportExport;
#endif
#ifdef DEBUG_MODE_SENSE
- fprintf(stderr,"NumMediumTransport=%d\n",retval->NumMediumTransport);
- fprintf(stderr,"NumStorage=%d\n",retval->NumStorage);
- fprintf(stderr,"NumImportExport=%d\n",retval->NumImportExport);
- fprintf(stderr,"NumDataTransfer=%d\n",retval->NumDataTransfer);
- fprintf(stderr,"MaxReadElementStatusData=%d\n",retval->MaxReadElementStatusData);
- fprintf(stderr,"NumElements=%d\n",retval->NumElements);
- fflush(stderr);
+ fprintf(stderr, "NumMediumTransport=%d\n", retval->NumMediumTransport);
+ fprintf(stderr, "NumStorage=%d\n", retval->NumStorage);
+ fprintf(stderr, "NumImportExport=%d\n", retval->NumImportExport);
+ fprintf(stderr, "NumDataTransfer=%d\n", retval->NumDataTransfer);
+ fprintf(stderr, "MaxReadElementStatusData=%d\n", retval->MaxReadElementStatusData);
+ fprintf(stderr, "NumElements=%d\n", retval->NumElements);
+ fflush(stderr);
#endif
-
- return retval;
+
+ return retval;
}
static void FreeElementData(ElementStatus_T *data)
{
- free(data->DataTransferElementAddress);
- free(data->DataTransferElementSourceStorageElementNumber);
- free(data->DataTransferPrimaryVolumeTag);
- free(data->DataTransferAlternateVolumeTag);
- free(data->PrimaryVolumeTag);
- free(data->AlternateVolumeTag);
- free(data->StorageElementAddress);
- free(data->StorageElementIsImportExport);
- free(data->StorageElementFull);
- free(data->DataTransferElementFull);
+ free(data->DataTransferElementAddress);
+ free(data->DataTransferElementSourceStorageElementNumber);
+ free(data->DataTransferPrimaryVolumeTag);
+ free(data->DataTransferAlternateVolumeTag);
+ free(data->PrimaryVolumeTag);
+ free(data->AlternateVolumeTag);
+ free(data->StorageElementAddress);
+ free(data->StorageElementIsImportExport);
+ free(data->StorageElementFull);
+ free(data->DataTransferElementFull);
}
-
-
+
/* allocate data */
-static ElementStatus_T *AllocateElementData(ElementModeSense_T *mode_sense) {
- ElementStatus_T *retval;
-
- retval=(ElementStatus_T *) xzmalloc(sizeof(ElementStatus_T));
-
- /* now for the invidual component arrays.... */
-
- retval->DataTransferElementAddress = (int *) xzmalloc(sizeof(int)*
- (mode_sense->NumDataTransfer + 1));
- retval->DataTransferElementSourceStorageElementNumber =
- (int *) xzmalloc(sizeof(int)*(mode_sense->NumDataTransfer+1));
- retval->DataTransferPrimaryVolumeTag=
- (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumDataTransfer+1));
- retval->DataTransferAlternateVolumeTag=
- (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumDataTransfer+1));
- retval->PrimaryVolumeTag=
- (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumStorage+1));
- retval->AlternateVolumeTag=
- (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumStorage+1));
- retval->StorageElementAddress=
- (int *)xzmalloc(sizeof(int)*(mode_sense->NumStorage+1));
- retval->StorageElementIsImportExport=
- (boolean *)xzmalloc(sizeof(boolean)*(mode_sense->NumStorage+1));
- retval->StorageElementFull=
- (boolean *)xzmalloc(sizeof(boolean)*(mode_sense->NumStorage+1));
- retval->DataTransferElementFull=
- (boolean *)xzmalloc(sizeof(boolean)*(mode_sense->NumDataTransfer+1));
-
- return retval; /* sigh! */
+static ElementStatus_T *AllocateElementData(ElementModeSense_T *mode_sense)
+{
+ ElementStatus_T *retval;
+
+ retval = (ElementStatus_T *)xzmalloc(sizeof(ElementStatus_T));
+
+ /* now for the invidual component arrays.... */
+
+ retval->DataTransferElementAddress =
+ (int *)xzmalloc(sizeof(int) * (mode_sense->NumDataTransfer + 1));
+ retval->DataTransferElementSourceStorageElementNumber =
+ (int *)xzmalloc(sizeof(int) * (mode_sense->NumDataTransfer + 1));
+ retval->DataTransferPrimaryVolumeTag =
+ (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumDataTransfer + 1));
+ retval->DataTransferAlternateVolumeTag =
+ (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumDataTransfer + 1));
+ retval->PrimaryVolumeTag =
+ (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumStorage + 1));
+ retval->AlternateVolumeTag =
+ (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumStorage + 1));
+ retval->StorageElementAddress =
+ (int *)xzmalloc(sizeof(int) * (mode_sense->NumStorage + 1));
+ retval->StorageElementIsImportExport =
+ (boolean *)xzmalloc(sizeof(boolean) * (mode_sense->NumStorage + 1));
+ retval->StorageElementFull =
+ (boolean *)xzmalloc(sizeof(boolean) * (mode_sense->NumStorage + 1));
+ retval->DataTransferElementFull =
+ (boolean *)xzmalloc(sizeof(boolean) * (mode_sense->NumDataTransfer + 1));
+
+ return retval; /* sigh! */
}
-void copy_barcode(unsigned char *src, unsigned char *dest) {
- int i;
-
- for (i=0; i < 37; i++) {
- *dest++ = *src++;
- }
- *dest=0; /* null-terminate, sigh. */
-}
+void copy_barcode(unsigned char *src, unsigned char *dest)
+{
+ int i;
+
+ for (i=0; i < 36; i++)
+ {
+ *dest = *src++;
+ if ((*dest < 32) || (*dest > 127))
+ {
+ *dest = '\0';
+ }
+
+ dest++;
+ }
+ *dest = 0; /* null-terminate */
+}
/* This #%!@# routine has more parameters than I can count! */
-unsigned char *SendElementStatusRequest(DEVICE_TYPE MediumChangerFD,
+static unsigned char *SendElementStatusRequestActual(
+ DEVICE_TYPE MediumChangerFD,
RequestSense_T *RequestSense,
Inquiry_T *inquiry_info,
SCSI_Flags_T *flags,
int ElementStart,
int NumElements,
int NumBytes
- ) {
-
- CDB_T CDB;
- boolean is_attached = false;
+ )
+{
+ CDB_T CDB;
+ boolean is_attached = false;
- unsigned char *DataBuffer; /* size of data... */
+ unsigned char *DataBuffer; /* size of data... */
#ifdef HAVE_GET_ID_LUN
- scsi_id_t *scsi_id;
+ scsi_id_t *scsi_id;
#endif
- if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {
- is_attached=true;
- }
- if (flags->no_attached) { /* override, sigh */
- is_attached=false;
- }
+ if (inquiry_info->MChngr &&
+ inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)
+ {
+ is_attached = true;
+ }
+
+ if (flags->no_attached)
+ {
+ /* override, sigh */
+ is_attached = false;
+ }
- DataBuffer=(unsigned char *) xmalloc(NumBytes+1);
+ DataBuffer = (unsigned char *)xzmalloc(NumBytes + 1);
+
+ slow_bzero((char *)RequestSense, sizeof(RequestSense_T));
- slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
#ifdef HAVE_GET_ID_LUN
- scsi_id = SCSI_GetIDLun(MediumChangerFD);
+ scsi_id = SCSI_GetIDLun(MediumChangerFD);
#endif
+ CDB[0] = 0xB8; /* READ ELEMENT STATUS */
+
+ if (is_attached)
+ {
+ CDB[0] = 0xB4; /* whoops, READ_ELEMENT_STATUS_ATTACHED! */
+ }
- CDB[0] = 0xB8; /* READ ELEMENT STATUS */
- if (is_attached) {
- CDB[0] = 0xB4; /* whoops, READ_ELEMENT_STATUS_ATTACHED! */
- }
#ifdef HAVE_GET_ID_LUN
- CDB[1] = (scsi_id->lun << 5) | ((flags->no_barcodes) ? 0 : 0x10) | flags->elementtype; /* Lun + VolTag + Type code */
- free(scsi_id);
+ CDB[1] = (scsi_id->lun << 5) | ((flags->no_barcodes) ?
+ 0 : 0x10) | flags->elementtype; /* Lun + VolTag + Type code */
+ free(scsi_id);
#else
- CDB[1] = ((flags->no_barcodes) ? 0 : 0x10) | flags->elementtype; /* Element Type Code = 0, VolTag = 1 */
+ /* Element Type Code = 0, VolTag = 1 */
+ CDB[1] = (unsigned char)((flags->no_barcodes ? 0 : 0x10) | flags->elementtype);
#endif
- CDB[2] = (ElementStart >> 8) & 0xff; /* Starting Element Address MSB */
- CDB[3] = ElementStart & 0xff; /* Starting Element Address LSB */
+ /* Starting Element Address */
+ CDB[2] = (unsigned char)(ElementStart >> 8);
+ CDB[3] = (unsigned char)ElementStart;
+
+ /* Number Of Elements */
+ CDB[4]= (unsigned char)(NumElements >> 8);
+ CDB[5]= (unsigned char)NumElements;
+ CDB[6] = 0; /* Reserved */
- CDB[4]= (NumElements >> 8) & 0xff; /* Number Of Elements MSB */
- CDB[5]= NumElements & 0xff ; /* Number of elements LSB */
+ /* allocation length */
+ CDB[7]= (unsigned char)(NumBytes >> 16);
+ CDB[8]= (unsigned char)(NumBytes >> 8);
+ CDB[9]= (unsigned char)NumBytes;
+
+ CDB[10] = 0; /* Reserved */
+ CDB[11] = 0; /* Control */
+
+#ifdef DEBUG_BARCODE
+ fprintf(stderr,"CDB:\n");
+ PrintHex(2, CDB, 12);
+#endif
+
+ if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
+ DataBuffer,NumBytes, RequestSense) != 0)
+ {
- /* CDB[5]=127; */ /* test */
#ifdef DEBUG
- fprintf(stderr,"CDB[5]=%d\n" , CDB[5]);
+ fprintf(stderr, "Read Element Status (0x%02X) failed\n", CDB[0]);
#endif
- CDB[6] = 0; /* Reserved */
- CDB[7]= (NumBytes >> 16) & 0xff; /* allocation length MSB */
- CDB[8]= (NumBytes >> 8) & 0xff;
- CDB[9]= NumBytes & 0xff; /* allocation length LSB */
+ /*
+ First see if we have sense key of 'illegal request',
+ additional sense code of '24', additional sense qualfier of
+ '0', and field in error of '4'. This means that we issued a request
+ w/bar code reader and did not have one, thus must re-issue the request
+ w/out barcode :-(.
+ */
+
+ /*
+ Most autochangers and tape libraries set a sense key here if
+ they do not have a bar code reader. For some reason, the ADIC DAT
+ uses a different sense key? Let's retry w/o bar codes for *ALL*
+ sense keys.
+ */
+
+ if (RequestSense->SenseKey > 1)
+ {
+ /* we issued a code requesting bar codes, there is no bar code reader? */
+ /* clear out our sense buffer first... */
+ slow_bzero((char *)RequestSense, sizeof(RequestSense_T));
- CDB[10] = 0; /* Reserved */
- CDB[11] = 0; /* Control */
+ CDB[1] &= ~0x10; /* clear bar code flag! */
#ifdef DEBUG_BARCODE
- {
- int i;
- fprintf(stderr,"CDB= ");
- for (i=0;i<12;i++) {
- fprintf(stderr,"%x ",CDB[i]);
- }
- fprintf(stderr,"\n");
- fflush(stderr);
- }
+ fprintf(stderr,"CDB:\n");
+ PrintHex(2, CDB, 12);
#endif
- if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
- DataBuffer,NumBytes, RequestSense) != 0){
- /* okay, first see if we have sense key of 'illegal request',
- additional sense code of '24', additional sense qualfier of
- '0', and field in error of '4'. This means that we issued a request
- w/bar code reader and did not have one, thus must re-issue the request
- w/out barcode :-(.
- */
-
- /* Okay, most autochangers and tape libraries set a sense key here if
- * they do not have a bar code reader. For some reason, the ADIC DAT
- * uses a different sense key? Let's retry w/o bar codes for *ALL*
- * sense keys, sigh sigh sigh.
- */
-
- if ( RequestSense->SenseKey > 1 ) {
- /* we issued a code requesting bar codes, there is no bar code reader? */
- /* clear out our sense buffer first... */
- slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
-
- CDB[1] &= ~0x10; /* clear bar code flag! */
-
-#ifdef DEBUG_BARCODE
- {
- int i;
- fprintf(stderr,"CDB= ");
- for (i=0;i<12;i++) {
- fprintf(stderr,"%x ",CDB[i]);
+ if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
+ DataBuffer, NumBytes, RequestSense) != 0)
+ {
+ free(DataBuffer);
+ return NULL;
+ }
+ }
+ else
+ {
+ free(DataBuffer);
+ return NULL;
+ }
}
- fprintf(stderr,"\n");
- fflush(stderr);
- }
-#endif
-
- if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
- DataBuffer, NumBytes, RequestSense) != 0){
- free(DataBuffer);
- return NULL;
- }
- } else {
- free(DataBuffer);
- return NULL;
- }
- }
#ifdef DEBUG_BARCODE
- /* print a bunch of extra debug data :-(. */
- PrintRequestSense(RequestSense); /* see what it sez :-(. */
- {
- int i;
- fprintf(stderr,"Data:");
- for (i=0;i<40;i++) {
- fprint(stderr,"%02x ",DataBuffer[i]);
- }
- fprintf(stderr,"\n");
- }
+ /* print a bunch of extra debug data :-(. */
+ PrintRequestSense(RequestSense); /* see what it sez :-(. */
+ fprintf(stderr,"Data:\n");
+ PrintHex(2, DataBuffer, 40);
#endif
- return DataBuffer; /* we succeeded! */
+ return DataBuffer; /* we succeeded! */
+}
+
+
+unsigned char *SendElementStatusRequest(DEVICE_TYPE MediumChangerFD,
+ RequestSense_T *RequestSense,
+ Inquiry_T *inquiry_info,
+ SCSI_Flags_T *flags,
+ int ElementStart,
+ int NumElements,
+ int NumBytes
+ )
+{
+ unsigned char *DataBuffer; /* size of data... */
+ int real_numbytes;
+
+ DataBuffer = SendElementStatusRequestActual(MediumChangerFD,
+ RequestSense,
+ inquiry_info,
+ flags,
+ ElementStart,
+ NumElements,
+ NumBytes
+ );
+ /*
+ One weird loader wants either 8 or BYTE_COUNT_OF_REPORT
+ values for the ALLOCATION_LENGTH. Give it what it wants
+ if we get an Sense Key of 05 Illegal Request with a
+ CDB position of 7 as the field in error.
+ */
+
+ if (DataBuffer == NULL &&
+ RequestSense->SenseKey == 5 &&
+ RequestSense->CommandData &&
+ RequestSense->BitPointer == 7)
+ {
+ NumBytes=8; /* send an 8 byte request */
+ DataBuffer=SendElementStatusRequestActual( MediumChangerFD,
+ RequestSense,
+ inquiry_info,
+ flags,
+ ElementStart,
+ NumElements,
+ NumBytes
+ );
+ }
+
+ /* the above code falls thru into this: */
+ if (DataBuffer != NULL)
+ {
+ /* see if we need to retry with a bigger NumBytes: */
+ real_numbytes = ((int)DataBuffer[5] << 16) +
+ ((int)DataBuffer[6] << 8) +
+ (int)DataBuffer[7] + 8;
+
+ if (real_numbytes > NumBytes)
+ {
+ /* uh-oh, retry! */
+ free(DataBuffer); /* solve memory leak */
+ DataBuffer = SendElementStatusRequestActual(MediumChangerFD,
+ RequestSense,
+ inquiry_info,
+ flags,
+ ElementStart,
+ NumElements,
+ real_numbytes
+ );
+ }
+ }
+
+ return DataBuffer;
}
+
+
/******************* ParseElementStatus ***********************************/
/* This does the actual grunt work of parsing element status data. It fills
* in appropriate pieces of its input data. It may be called multiple times
* while we are gathering element status.
*/
-static void ParseElementStatus(int *EmptyStorageElementAddress,
- int *EmptyStorageElementCount,
- unsigned char *DataBuffer,
- ElementStatus_T *ElementStatus,
- ElementModeSense_T *mode_sense
-
- ) {
-
- unsigned char *DataPointer=DataBuffer;
- TransportElementDescriptor_T TEBuf;
- TransportElementDescriptor_T *TransportElementDescriptor;
- ElementStatusDataHeader_T *ElementStatusDataHeader;
- Element2StatusPage_T *ElementStatusPage;
- Element2StatusPage_T ESBuf;
- int ElementCount;
- int TransportElementDescriptorLength;
- int BytesAvailable;
-
- ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataBuffer;
-
-
-
- ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataPointer;
- DataPointer += sizeof(ElementStatusDataHeader_T);
- ElementCount =
- BigEndian16(ElementStatusDataHeader->NumberOfElementsAvailable);
+static void ParseElementStatus( int *EmptyStorageElementAddress,
+ int *EmptyStorageElementCount,
+ unsigned char *DataBuffer,
+ ElementStatus_T *ElementStatus,
+ ElementModeSense_T *mode_sense,
+ int *pNextElement
+ )
+{
+ unsigned char *DataPointer = DataBuffer;
+ TransportElementDescriptor_T TEBuf;
+ TransportElementDescriptor_T *TransportElementDescriptor;
+ ElementStatusDataHeader_T *ElementStatusDataHeader;
+ Element2StatusPage_T *ElementStatusPage;
+ Element2StatusPage_T ESBuf;
+ int ElementCount;
+ int TransportElementDescriptorLength;
+ int BytesAvailable;
+ int ImportExportIndex;
+
+ ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataPointer;
+ DataPointer += sizeof(ElementStatusDataHeader_T);
+ ElementCount =
+ BigEndian16(ElementStatusDataHeader->NumberOfElementsAvailable);
+
#ifdef DEBUG
- fprintf(stderr,"ElementCount=%d\n",ElementCount);
- fflush(stderr);
+ fprintf(stderr,"ElementCount=%d\n",ElementCount);
+ fflush(stderr);
#endif
- while (ElementCount > 0)
- {
+
+ while (ElementCount > 0)
+ {
#ifdef DEBUG
- int got_element_num=0;
-
- fprintf(stderr,"Working on element # %d Element Count %d\n",got_element_num,ElementCount);
- got_element_num++;
+ int got_element_num=0;
+
+ fprintf(stderr,"Working on element # %d Element Count %d\n",got_element_num,ElementCount);
+ got_element_num++;
#endif
- memcpy(&ESBuf, DataPointer, sizeof(ElementStatusPage_T));
- ElementStatusPage = &ESBuf;
- DataPointer += sizeof(ElementStatusPage_T);
- TransportElementDescriptorLength =
- BigEndian16(ElementStatusPage->ElementDescriptorLength);
- if (TransportElementDescriptorLength <
- sizeof(TransportElementDescriptorShort_T)) {
-
- /* Foo, Storage Element Descriptors can be 4 bytes?! */
- if ( (ElementStatusPage->ElementTypeCode != MediumTransportElement &&
- ElementStatusPage->ElementTypeCode != StorageElement &&
- ElementStatusPage->ElementTypeCode != ImportExportElement ) ||
- TransportElementDescriptorLength < 4) {
+ memcpy(&ESBuf, DataPointer, sizeof(ElementStatusPage_T));
+ ElementStatusPage = &ESBuf;
+ DataPointer += sizeof(ElementStatusPage_T);
+
+ TransportElementDescriptorLength =
+ BigEndian16(ElementStatusPage->ElementDescriptorLength);
+
+ if (TransportElementDescriptorLength <
+ sizeof(TransportElementDescriptorShort_T))
+ {
+ /* Foo, Storage Element Descriptors can be 4 bytes?! */
+ if ((ElementStatusPage->ElementTypeCode != MediumTransportElement &&
+ ElementStatusPage->ElementTypeCode != StorageElement &&
+ ElementStatusPage->ElementTypeCode != ImportExportElement ) ||
+ TransportElementDescriptorLength < 4)
+ {
#ifdef DEBUG
- fprintf(stderr,"boom! ElementTypeCode=%d\n",ElementStatusPage->ElementTypeCode);
+ fprintf(stderr,"boom! ElementTypeCode=%d\n",ElementStatusPage->ElementTypeCode);
#endif
- FatalError("Transport Element Descriptor Length too short: %d\n",TransportElementDescriptorLength);
- }
-
- }
+ FatalError("Transport Element Descriptor Length too short: %d\n", TransportElementDescriptorLength);
+ }
+ }
#ifdef DEBUG
- fprintf(stderr,"Transport Element Descriptor Length=%d\n",TransportElementDescriptorLength);
+ fprintf(stderr,"Transport Element Descriptor Length=%d\n",TransportElementDescriptorLength);
#endif
- BytesAvailable =
- BigEndian24(ElementStatusPage->ByteCountOfDescriptorDataAvailable);
- if (BytesAvailable <= 0) {
- ElementCount--;
- }
- while (BytesAvailable > 0)
- {
- /* TransportElementDescriptor =
- (TransportElementDescriptor_T *) DataPointer; */
- memcpy(&TEBuf, DataPointer,
- (TransportElementDescriptorLength <= sizeof(TEBuf)) ?
- TransportElementDescriptorLength :
- sizeof(TEBuf));
- TransportElementDescriptor = &TEBuf;
-
- DataPointer += TransportElementDescriptorLength;
- BytesAvailable -= TransportElementDescriptorLength;
- ElementCount--;
- switch (ElementStatusPage->ElementTypeCode)
- {
- case MediumTransportElement:
- ElementStatus->TransportElementAddress = BigEndian16(TransportElementDescriptor->ElementAddress);
+ BytesAvailable =
+ BigEndian24(ElementStatusPage->ByteCountOfDescriptorDataAvailable);
#ifdef DEBUG
- fprintf(stderr,"TransportElementAddress=%d\n",ElementStatus->TransportElementAddress);
+ fprintf(stderr,"%d bytes of descriptor data available in descriptor\n",
+ BytesAvailable);
#endif
- break;
- /* we treat ImportExport elements as if they were normal
- * storage elements now, sigh...
- */
- case ImportExportElement:
- ElementStatus->ImportExportCount++;
+ /* work around a bug in ADIC DAT loaders */
+ if (BytesAvailable <= 0)
+ {
+ ElementCount--; /* sorry :-( */
+ }
+ while (BytesAvailable > 0)
+ {
+ /* TransportElementDescriptor =
+ (TransportElementDescriptor_T *) DataPointer; */
+ memcpy(&TEBuf, DataPointer,
+ (TransportElementDescriptorLength <= sizeof(TEBuf)) ?
+ TransportElementDescriptorLength :
+ sizeof(TEBuf));
+ TransportElementDescriptor = &TEBuf;
+
+ if (pNextElement != NULL)
+ {
+ if (BigEndian16(TransportElementDescriptor->ElementAddress) != 0 || *pNextElement == 0)
+ {
+ (*pNextElement) = BigEndian16(TransportElementDescriptor->ElementAddress) + 1;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ DataPointer += TransportElementDescriptorLength;
+ BytesAvailable -= TransportElementDescriptorLength;
+ ElementCount--;
+
+ switch (ElementStatusPage->ElementTypeCode)
+ {
+ case MediumTransportElement:
+ ElementStatus->TransportElementAddress = BigEndian16(TransportElementDescriptor->ElementAddress);
#ifdef DEBUG
- fprintf(stderr,"ImportExportElement=%d\n",ElementStatus->StorageElementCount);
+ fprintf(stderr,"TransportElementAddress=%d\n",ElementStatus->TransportElementAddress);
#endif
- ElementStatus->StorageElementIsImportExport[ElementStatus->StorageElementCount] = 1;
- /* WARNING: DO *NOT* PUT A 'break' STATEMENT HERE, it is supposed
- * to run on into the case for a Storage Element!
- */
- case StorageElement:
+ break;
+
+ /* we treat ImportExport elements as if they were normal
+ * storage elements now, sigh...
+ */
+ case ImportExportElement:
#ifdef DEBUG
- fprintf(stderr,"StorageElementCount=%d ElementAddress = %d ",ElementStatus->StorageElementCount,BigEndian16(TransportElementDescriptor->ElementAddress));
+ fprintf(stderr,"ImportExportElement=%d\n",ElementStatus->StorageElementCount);
#endif
- ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount] =
- BigEndian16(TransportElementDescriptor->ElementAddress);
- ElementStatus->StorageElementFull[ElementStatus->StorageElementCount] =
- TransportElementDescriptor->Full;
+ if (ElementStatus->ImportExportCount >= mode_sense->NumImportExport)
+ {
+ fprintf(stderr,"Warning:Too Many Import/Export Elements Reported (expected %d, now have %d\n",
+ mode_sense->NumImportExport,
+ ElementStatus->ImportExportCount + 1);
+ fflush(stderr);
+ return; /* we're done :-(. */
+ }
+
+ ImportExportIndex = mode_sense->NumStorage - mode_sense->NumImportExport + ElementStatus->ImportExportCount;
+
+ ElementStatus->StorageElementAddress[ImportExportIndex] =
+ BigEndian16(TransportElementDescriptor->ElementAddress);
+ ElementStatus->StorageElementFull[ImportExportIndex] =
+ TransportElementDescriptor->Full;
+
+ if ( (TransportElementDescriptorLength > 11) &&
+ (ElementStatusPage->VolBits & E2_AVOLTAG))
+ {
+ copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
+ ElementStatus->AlternateVolumeTag[ImportExportIndex]);
+ }
+ else
+ {
+ ElementStatus->AlternateVolumeTag[ImportExportIndex][0] = 0; /* null string. */;
+ }
+ if ((TransportElementDescriptorLength > 11) &&
+ (ElementStatusPage->VolBits & E2_PVOLTAG))
+ {
+ copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
+ ElementStatus->PrimaryVolumeTag[ImportExportIndex]);
+ }
+ else
+ {
+ ElementStatus->PrimaryVolumeTag[ImportExportIndex][0]=0; /* null string. */
+ }
+
+ ElementStatus->StorageElementIsImportExport[ImportExportIndex] = 1;
+
+ ElementStatus->ImportExportCount++;
+ break;
+
+ case StorageElement:
#ifdef DEBUG
- fprintf(stderr,"TransportElement->Full = %d\n",TransportElementDescriptor->Full);
+ fprintf(stderr,"StorageElementCount=%d ElementAddress = %d ",ElementStatus->StorageElementCount,BigEndian16(TransportElementDescriptor->ElementAddress));
#endif
- if (!TransportElementDescriptor->Full)
- {
- EmptyStorageElementAddress[(*EmptyStorageElementCount)++] =
- ElementStatus->StorageElementCount; /* slot idx. */
- /* ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount]; */
+ /* ATL/Exabyte kludge -- skip slots that aren't installed :-( */
+ if (TransportElementDescriptor->AdditionalSenseCode==0x83 &&
+ TransportElementDescriptor->AdditionalSenseCodeQualifier==0x02)
+ continue;
+
+ ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount] =
+ BigEndian16(TransportElementDescriptor->ElementAddress);
+ ElementStatus->StorageElementFull[ElementStatus->StorageElementCount] =
+ TransportElementDescriptor->Full;
+#ifdef DEBUG
+ if (TransportElementDescriptor->Except)
+ fprintf(stderr,"ASC,ASCQ = 0x%x,0x%x ",TransportElementDescriptor->AdditionalSenseCode,TransportElementDescriptor->AdditionalSenseCodeQualifier);
+ fprintf(stderr,"TransportElement->Full = %d\n",TransportElementDescriptor->Full);
+#endif
+ if (!TransportElementDescriptor->Full)
+ {
+ EmptyStorageElementAddress[(*EmptyStorageElementCount)++] =
+ ElementStatus->StorageElementCount; /* slot idx. */
+ /* ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount]; */
+ }
+ if ((TransportElementDescriptorLength > 11) &&
+ (ElementStatusPage->VolBits & E2_AVOLTAG))
+ {
+ copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
+ ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount]);
+ }
+ else
+ {
+ ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */;
+ }
+ if ((TransportElementDescriptorLength > 11) &&
+ (ElementStatusPage->VolBits & E2_PVOLTAG))
+ {
+ copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
+ ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount]);
+ }
+ else
+ {
+ ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */
+ }
+
+ ElementStatus->StorageElementCount++;
+ /*
+ Note that the original mtx had no check here for
+ buffer overflow, though some drives might mistakingly
+ do one...
+ */
+
+ if (ElementStatus->StorageElementCount > mode_sense->NumStorage)
+ {
+ fprintf(stderr,"Warning:Too Many Storage Elements Reported (expected %d, now have %d\n",
+ mode_sense->NumStorage,
+ ElementStatus->StorageElementCount);
+ fflush(stderr);
+ return; /* we're done :-(. */
+ }
+ break;
+
+ case DataTransferElement:
+ /* tape drive not installed, go back to top of loop */
+
+ /* if (TransportElementDescriptor->Except) continue ; */
+
+ /* Note: This is for Exabyte tape libraries that improperly
+ report that they have a 2nd tape drive when they don't. We
+ could generalize this in an ideal world, but my attempt to
+ do so failed with dual-drive Exabyte tape libraries that
+ *DID* have the second drive. Sigh.
+ */
+ if (TransportElementDescriptor->AdditionalSenseCode==0x83 &&
+ TransportElementDescriptor->AdditionalSenseCodeQualifier==0x04)
+ {
+ continue;
+ }
+
+ /* generalize it. Does it work? Let's try it! */
+ /*
+ No, dammit, following does not work on dual-drive Exabyte
+ 'cause if a tape is in the drive, it sets the AdditionalSense
+ code to something (sigh).
+ */
+ /* if (TransportElementDescriptor->AdditionalSenseCode!=0)
+ continue;
+ */
+
+ ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount] =
+ BigEndian16(TransportElementDescriptor->ElementAddress);
+ ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount] =
+ TransportElementDescriptor->Full;
+ ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount] =
+ BigEndian16(TransportElementDescriptor->SourceStorageElementAddress);
+
+#if DEBUG
+ fprintf(stderr, "%d: ElementAddress = %d, Full = %d, SourceElement = %d\n",
+ ElementStatus->DataTransferElementCount,
+ ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount],
+ ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount],
+ ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount]);
+#endif
+ if (ElementStatus->DataTransferElementCount >= mode_sense->NumDataTransfer)
+ {
+ FatalError("Too many Data Transfer Elements Reported\n");
+ }
+
+ if (ElementStatusPage->VolBits & E2_PVOLTAG)
+ {
+ copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
+ ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount]);
+ }
+ else
+ {
+ ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
+ }
+
+ if (ElementStatusPage->VolBits & E2_AVOLTAG)
+ {
+ copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
+ ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount]);
+ }
+ else
+ {
+ ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
+ }
+
+ ElementStatus->DataTransferElementCount++;
+
+ /* 0 actually is a usable element address */
+ /* if (DataTransferElementAddress == 0) */
+ /* FatalError( */
+ /* "illegal Data Transfer Element Address %d reported\n", */
+ /* DataTransferElementAddress); */
+ break;
+
+ default:
+ FatalError("illegal Element Type Code %d reported\n",
+ ElementStatusPage->ElementTypeCode);
+ }
}
- if ( (TransportElementDescriptorLength > 11) &&
- (ElementStatusPage->VolBits & E2_AVOLTAG)) {
- copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
- ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount]);
- } else {
- ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */;
- }
- if ( (TransportElementDescriptorLength > 11) &&
- (ElementStatusPage->VolBits & E2_PVOLTAG)) {
- copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
- ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount]);
- } else {
- ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */
- }
-
- ElementStatus->StorageElementCount++;
- /* Note that the original mtx had no check here for
- buffer overflow, though some drives might mistakingly
- do one...
- */
- if (ElementStatus->StorageElementCount > mode_sense->NumStorage) {
- fprintf(stderr,"Warning:Too Many Storage Elements Reported (expected %d, now have %d\n",
- mode_sense->NumStorage,
- ElementStatus->StorageElementCount);
- fflush(stderr);
- return; /* we're done :-(. */
- }
-
-
- break;
-
-
- case DataTransferElement:
- /* tape drive not installed, go back to top of loop */
-
- /* if (TransportElementDescriptor->Except) continue ; */
-
- /* Note: This is for Exabyte tape libraries that improperly
- report that they have a 2nd tape drive when they don't. We
- could generalize this in an ideal world, but my attempt to
- do so failed with dual-drive Exabyte tape libraries that
- *DID* have the second drive. Sigh.
- */
- /* No longer need this test due to code restructuring? */
- /* if (TransportElementDescriptor->AdditionalSenseCode==0x83 &&
- TransportElementDescriptor->AdditionalSenseCodeQualifier==0x04)
- continue;
- */
- /* generalize it. Does it work? Let's try it! */
- /* No, dammit, following does not work on dual-drive Exabyte
- 'cause if a tape is in the drive, it sets the AdditionalSense
- code to something (sigh).
- */
- /* if (TransportElementDescriptor->AdditionalSenseCode!=0)
- continue;
- */
-
-
-
- ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount] =
- BigEndian16(TransportElementDescriptor->ElementAddress);
- ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount] =
- TransportElementDescriptor->Full;
- ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount] =
- BigEndian16(TransportElementDescriptor
- ->SourceStorageElementAddress);
-
- if (ElementStatus->DataTransferElementCount >= mode_sense->NumDataTransfer) {
- FatalError("Too many Data Transfer Elements Reported\n");
- }
- if (ElementStatusPage->VolBits & E2_PVOLTAG) {
- copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
- ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount]);
- } else {
- ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
- }
- if (ElementStatusPage->VolBits & E2_AVOLTAG) {
- copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
- ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount]);
- } else {
- ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
- }
- ElementStatus->DataTransferElementCount++;
- /* 0 actually is a usable element address */
- /* if (DataTransferElementAddress == 0) */
- /* FatalError( */
- /* "illegal Data Transfer Element Address %d reported\n", */
- /* DataTransferElementAddress); */
- break;
- default:
- FatalError("illegal Element Type Code %d reported\n",
- ElementStatusPage->ElementTypeCode);
- }
}
- }
+
+#ifdef DEBUG
+ if (pNextElement)
+ fprintf(stderr,"Next start element will be %d\n",*pNextElement);
+#endif
}
+
/********************* Real ReadElementStatus ********************* */
/*
*/
-ElementStatus_T *ReadElementStatus(DEVICE_TYPE MediumChangerFD, RequestSense_T *RequestSense, Inquiry_T *inquiry_info, SCSI_Flags_T *flags) {
- ElementStatus_T *ElementStatus;
-
- unsigned char *DataBuffer; /* size of data... */
-
- int EmptyStorageElementCount=0;
- int *EmptyStorageElementAddress; /* [MAX_STORAGE_ELEMENTS]; */
-
- int empty_idx=0;
- int invalid_sources=0;
- boolean is_attached = false;
- int i,j;
-
- ElementModeSense_T *mode_sense = NULL;
-
- if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {
- is_attached=true;
- }
- if (flags->no_attached) { /* override, sigh */
- is_attached=false;
- }
- if (!is_attached) {
- mode_sense=ReadAssignmentPage(MediumChangerFD);
- }
- if (!mode_sense) {
- mode_sense=(ElementModeSense_T *) xmalloc(sizeof(ElementModeSense_T));
- mode_sense->NumMediumTransport=MAX_TRANSPORT_ELEMENTS;
- mode_sense->NumStorage=MAX_STORAGE_ELEMENTS;
- mode_sense->NumDataTransfer=MAX_TRANSFER_ELEMENTS;
- mode_sense->MaxReadElementStatusData= (sizeof(ElementStatusDataHeader_T)
- + 3 * sizeof(ElementStatusPage_T)
- + (MAX_STORAGE_ELEMENTS+MAX_TRANSFER_ELEMENTS+MAX_TRANSPORT_ELEMENTS)
- * sizeof(TransportElementDescriptor_T));
-
- /* don't care about the others anyhow at the moment... */
- }
-
- ElementStatus=AllocateElementData(mode_sense);
-
- /* Now to initialize it (sigh). */
- ElementStatus->StorageElementCount = 0;
- ElementStatus->DataTransferElementCount=0;
-
-
- /* first, allocate some empty storage stuff: Note that we pass this
- * down to ParseElementStatus (sigh!)
- */
-
- EmptyStorageElementAddress=(int *)xzmalloc((mode_sense->NumStorage+1)*sizeof(int));
- for (i=0;i<mode_sense->NumStorage;i++) {
- EmptyStorageElementAddress[i]=-1;
- }
-
- /* Okay, now to send some requests for the various types of stuff: */
-
- /* -----------STORAGE ELEMENTS---------------- */
- /* Let's start with storage elements: */
-
- for (i=0;i<mode_sense->NumDataTransfer;i++) {
- /* initialize them to an illegal # so that we can fix later... */
- ElementStatus->DataTransferElementSourceStorageElementNumber[i] = -1;
- }
-
-
- flags->elementtype=StorageElement; /* sigh! */
- DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
- inquiry_info,flags,
- mode_sense->StorageStart,
- /* adjust for import/export. */
- mode_sense->NumStorage-mode_sense->NumImportExport,
- mode_sense->MaxReadElementStatusData);
- if (!DataBuffer) {
- /* darn. Free up stuff and return. */
- /****FIXME**** do a free on element data! */
- FreeElementData(ElementStatus);
- return NULL;
- }
-
- ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
- DataBuffer,ElementStatus,mode_sense);
-
- free(DataBuffer); /* sigh! */
-
- /* --------------IMPORT/EXPORT--------------- */
- /* Next let's see if we need to do Import/Export: */
- if (mode_sense->NumImportExport > 0) {
- flags->elementtype=ImportExportElement;
- DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
- inquiry_info,flags,
- mode_sense->ImportExportStart,
- mode_sense->NumImportExport,
- mode_sense->MaxReadElementStatusData);
-
- if (!DataBuffer) {
- /* darn. Free up stuff and return. */
- /****FIXME**** do a free on element data! */
- FreeElementData(ElementStatus);
- return NULL;
- }
- ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
- DataBuffer,ElementStatus,mode_sense);
-
- }
-
-
-
- /* ----------------- DRIVES ---------------------- */
-
-
- flags->elementtype=DataTransferElement; /* sigh! */
- DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
- inquiry_info,flags,
- mode_sense->DataTransferStart,
- mode_sense->NumDataTransfer,
- mode_sense->MaxReadElementStatusData);
- if (!DataBuffer) {
- /* darn. Free up stuff and return. */
- /****FIXME**** do a free on element data! */
- FreeElementData(ElementStatus);
- return NULL;
- }
-
- ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
- DataBuffer,ElementStatus,mode_sense);
-
- free(DataBuffer); /* sigh! */
-
-
- /* ----------------- Robot Arm(s) -------------------------- */
-
- /* grr, damned brain dead HP doesn't report that it has any! */
- if (!mode_sense->NumMediumTransport) {
- ElementStatus->TransportElementAddress=0; /* default it sensibly :-(. */
- } else {
- flags->elementtype=MediumTransportElement; /* sigh! */
- DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
- inquiry_info,flags,
- mode_sense->MediumTransportStart,
- 1, /* only get 1, sigh. */
- mode_sense->MaxReadElementStatusData);
- if (!DataBuffer) {
- /* darn. Free up stuff and return. */
- /****FIXME**** do a free on element data! */
- FreeElementData(ElementStatus);
- return NULL;
- }
-
- ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
- DataBuffer,ElementStatus,mode_sense);
-
- free(DataBuffer); /* sigh! */
- }
-
- /*---------------------- Sanity Checking ------------------- */
-
- if (!ElementStatus->DataTransferElementCount)
- FatalError("no Data Transfer Element reported\n");
- if (ElementStatus->StorageElementCount == 0)
- FatalError("no Storage Elements reported\n");
-
-
- /* ---------------------- Reset SourceStorageElementNumbers ------- */
-
- /* okay, re-write the SourceStorageElementNumber code *AGAIN*.
- * Pass1:
- * Translate from raw element # to our translated # (if possible).
- * First, check the SourceStorageElementNumbers against the list of
- * filled slots. If the slots indicated are empty, we accept that list as
- * valid. Otherwise decide the SourceStorageElementNumbers are invalid.
- *
- * Pass2:
- *
- * If we had some invalid (or unknown) SourceStorageElementNumbers
- * then we must search for free slots, and assign SourceStorageElementNumbers
- * to those free slots. We happen to already built a list of free
- * slots as part of the process of reading the storage element numbers
- * from the tape. So that's easy enough to do!
- */
+ElementStatus_T *ReadElementStatus(DEVICE_TYPE MediumChangerFD, RequestSense_T *RequestSense, Inquiry_T *inquiry_info, SCSI_Flags_T *flags)
+{
+ ElementStatus_T *ElementStatus;
-#ifdef DEBUG_TAPELIST
- fprintf(stderr,"empty slots: %d\n",EmptyStorageElementCount);
- if (EmptyStorageElementCount) {
- for (i=0; i<EmptyStorageElementCount; i++) {
- fprintf(stderr,"empty: %d\n",EmptyStorageElementAddress[i]);
- }
- }
+ unsigned char *DataBuffer; /* size of data... */
+
+ int EmptyStorageElementCount=0;
+ int *EmptyStorageElementAddress; /* [MAX_STORAGE_ELEMENTS]; */
+
+ int empty_idx = 0;
+ boolean is_attached = false;
+ int i,j;
+
+ ElementModeSense_T *mode_sense = NULL;
+
+ if (inquiry_info->MChngr && inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)
+ {
+ is_attached = true;
+ }
+
+ if (flags->no_attached)
+ {
+ /* override, sigh */
+ is_attached = false;
+ }
+
+ if (!is_attached)
+ {
+ mode_sense = ReadAssignmentPage(MediumChangerFD);
+ }
+
+ if (!mode_sense)
+ {
+ mode_sense = (ElementModeSense_T *)xmalloc(sizeof(ElementModeSense_T));
+ mode_sense->NumMediumTransport = MAX_TRANSPORT_ELEMENTS;
+ mode_sense->NumStorage = MAX_STORAGE_ELEMENTS;
+ mode_sense->NumDataTransfer = MAX_TRANSFER_ELEMENTS;
+ mode_sense->MaxReadElementStatusData =
+ (sizeof(ElementStatusDataHeader_T) + 3 * sizeof(ElementStatusPage_T) +
+ (MAX_STORAGE_ELEMENTS+MAX_TRANSFER_ELEMENTS+MAX_TRANSPORT_ELEMENTS) *
+ sizeof(TransportElementDescriptor_T));
+
+ /* don't care about the others anyhow at the moment... */
+ }
+
+ ElementStatus = AllocateElementData(mode_sense);
+
+ /* Now to initialize it (sigh). */
+ ElementStatus->StorageElementCount = 0;
+ ElementStatus->DataTransferElementCount = 0;
+
+ /* first, allocate some empty storage stuff: Note that we pass this
+ * down to ParseElementStatus (sigh!)
+ */
+
+ EmptyStorageElementAddress = (int *)xzmalloc((mode_sense->NumStorage+1)*sizeof(int));
+ for (i = 0; i < mode_sense->NumStorage; i++)
+ {
+ EmptyStorageElementAddress[i] = -1;
+ }
+
+ /* Okay, now to send some requests for the various types of stuff: */
+
+ /* -----------STORAGE ELEMENTS---------------- */
+ /* Let's start with storage elements: */
+
+ for (i = 0; i < mode_sense->NumDataTransfer; i++)
+ {
+ /* initialize them to an illegal # so that we can fix later... */
+ ElementStatus->DataTransferElementSourceStorageElementNumber[i] = -1;
+ }
+
+ if (flags->querytype == MTX_ELEMENTSTATUS_ORIGINAL)
+ {
+#ifdef DEBUG
+ fprintf(stderr,"Using original element status polling method (storage, import/export, drivers etc independantly)\n");
+#endif
+ flags->elementtype = StorageElement; /* sigh! */
+ DataBuffer = SendElementStatusRequest( MediumChangerFD, RequestSense,
+ inquiry_info, flags,
+ mode_sense->StorageStart,
+ /* adjust for import/export. */
+ mode_sense->NumStorage - mode_sense->NumImportExport,
+ mode_sense->MaxReadElementStatusData);
+
+ if (!DataBuffer)
+ {
+#ifdef DEBUG
+ fprintf(stderr,"Had no elements!\n");
+#endif
+ /* darn. Free up stuff and return. */
+#ifdef DEBUG_MODE_SENSE
+ PrintRequestSense(RequestSense);
+#endif
+ FreeElementData(ElementStatus);
+ return NULL;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "Parsing storage elements\n");
+#endif
+ ParseElementStatus(EmptyStorageElementAddress, &EmptyStorageElementCount,
+ DataBuffer,ElementStatus,mode_sense,NULL);
+
+ free(DataBuffer); /* sigh! */
+
+ /* --------------IMPORT/EXPORT--------------- */
+ /* Next let's see if we need to do Import/Export: */
+ if (mode_sense->NumImportExport > 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr,"Sending request for Import/Export status\n");
+#endif
+ flags->elementtype = ImportExportElement;
+ DataBuffer = SendElementStatusRequest( MediumChangerFD,RequestSense,
+ inquiry_info, flags,
+ mode_sense->ImportExportStart,
+ mode_sense->NumImportExport,
+ mode_sense->MaxReadElementStatusData);
+
+ if (!DataBuffer)
+ {
+#ifdef DEBUG
+ fprintf(stderr,"Had no input/export element!\n");
+#endif
+ /* darn. Free up stuff and return. */
+#ifdef DEBUG_MODE_SENSE
+ PrintRequestSense(RequestSense);
+#endif
+ FreeElementData(ElementStatus);
+ return NULL;
+ }
+#ifdef DEBUG
+ fprintf(stderr,"Parsing inport/export element status\n");
+#endif
+#ifdef DEBUG_ADIC
+ dump_data(DataBuffer, 100); /* dump some data :-(. */
+#endif
+ ParseElementStatus( EmptyStorageElementAddress, &EmptyStorageElementCount,
+ DataBuffer, ElementStatus, mode_sense, NULL);
+
+ ElementStatus->StorageElementCount += ElementStatus->ImportExportCount;
+ }
+
+ /* ----------------- DRIVES ---------------------- */
+
+#ifdef DEBUG
+ fprintf(stderr,"Sending request for data transfer element (drive) status\n");
+#endif
+ flags->elementtype = DataTransferElement; /* sigh! */
+ DataBuffer = SendElementStatusRequest( MediumChangerFD, RequestSense,
+ inquiry_info, flags,
+ mode_sense->DataTransferStart,
+ mode_sense->NumDataTransfer,
+ mode_sense->MaxReadElementStatusData);
+ if (!DataBuffer)
+ {
+#ifdef DEBUG
+ fprintf(stderr,"No data transfer element status.");
+#endif
+ /* darn. Free up stuff and return. */
+#ifdef DEBUG_MODE_SENSE
+ PrintRequestSense(RequestSense);
#endif
+ FreeElementData(ElementStatus);
+ return NULL;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr,"Parsing data for data transfer element (drive) status\n");
+#endif
+ ParseElementStatus( EmptyStorageElementAddress, &EmptyStorageElementCount,
+ DataBuffer,ElementStatus, mode_sense, NULL);
+
+ free(DataBuffer); /* sigh! */
+
+ /* ----------------- Robot Arm(s) -------------------------- */
+
+ /* grr, damned brain dead HP doesn't report that it has any! */
+ if (!mode_sense->NumMediumTransport)
+ {
+ ElementStatus->TransportElementAddress = 0; /* default it sensibly :-(. */
+ }
+ else
+ {
+#ifdef DEBUG
+ fprintf(stderr,"Sending request for robot arm status\n");
+#endif
+ flags->elementtype = MediumTransportElement; /* sigh! */
+ DataBuffer = SendElementStatusRequest( MediumChangerFD, RequestSense,
+ inquiry_info, flags,
+ mode_sense->MediumTransportStart,
+ 1, /* only get 1, sigh. */
+ mode_sense->MaxReadElementStatusData);
+ if (!DataBuffer)
+ {
+#ifdef DEBUG
+ fprintf(stderr,"Loader reports no robot arm!\n");
+#endif
+ /* darn. Free up stuff and return. */
+#ifdef DEBUG_MODE_SENSE
+ PrintRequestSense(RequestSense);
+#endif
+ FreeElementData(ElementStatus);
+ return NULL;
+ }
+#ifdef DEBUG
+ fprintf(stderr,"Parsing robot arm data\n");
+#endif
+ ParseElementStatus( EmptyStorageElementAddress, &EmptyStorageElementCount,
+ DataBuffer, ElementStatus, mode_sense, NULL);
+
+ free(DataBuffer);
+ }
+ }
+ else
+ {
+ int nLastEl=-1, nNextEl=0;
+
+#ifdef DEBUG
+ fprintf(stderr,"Using alternative element status polling method (all elements)\n");
+#endif
+ /* ----------------- ALL Elements ---------------------- */
+ /* Just keep asking for elements till no more are returned
+ - increment our starting address as we go acording to the
+ number of elements returned from the last call
+ */
+
+ while(nLastEl!=nNextEl)
+ {
+ flags->elementtype = AllElementTypes;//StorageElement; /* sigh! */ /*XL1B2 firewire changer does not seem to respond to specific types so just use all elements*/
+ DataBuffer = SendElementStatusRequest( MediumChangerFD,
+ RequestSense,
+ inquiry_info,
+ flags,
+ nNextEl,//mode_sense->StorageStart,
+ /* adjust for import/export. */
+ mode_sense->NumStorage - mode_sense->NumImportExport,//FIX ME:this should be a more sensible value
+ mode_sense->MaxReadElementStatusData);
+ if (!DataBuffer)
+ {
+ if (RequestSense->AdditionalSenseCode == 0x21 &&
+ RequestSense->AdditionalSenseCodeQualifier == 0x01)
+ {
+ /* Error is invalid element address, we've probably just hit the end */
+ break;
+ }
+
+ /* darn. Free up stuff and return. */
+ FreeElementData(ElementStatus);
+ return NULL;
+ }
+
+ nLastEl = nNextEl;
+
+ ParseElementStatus( EmptyStorageElementAddress, &EmptyStorageElementCount,
+ DataBuffer, ElementStatus, mode_sense, &nNextEl);
+
+ free(DataBuffer); /* sigh! */
+ }
- /* Okay, now we re-assign origin slots if the "real" origin slot
- * is obviously defective:
- */
- /* pass one: */
- invalid_sources=0; /* no invalid sources yet! */
- for (i=0;i<ElementStatus->DataTransferElementCount;i++) {
- int elnum;
- int translated_elnum = -1;
- /* if we have an element, then ... */
- if (ElementStatus->DataTransferElementFull[i]) {
- elnum=ElementStatus->DataTransferElementSourceStorageElementNumber[i];
- /* if we have an element number, then ... */
- if (elnum >= 0) {
- /* Now to translate the elnum: */
- for (j=0; j<ElementStatus->StorageElementCount; j++) {
- if (elnum == ElementStatus->StorageElementAddress[j]) {
- translated_elnum=j;
- }
- }
- /* now see if the element # is already occupied: */
- if (ElementStatus->StorageElementFull[translated_elnum]) {
- invalid_sources=1;
- break; /* break out of the loop! */
- } else {
- /* properly set the source... */
- ElementStatus->DataTransferElementSourceStorageElementNumber[i]=
- translated_elnum;
+ ElementStatus->StorageElementCount += ElementStatus->ImportExportCount;
}
-
- } else {
- /* if element # was not >=0, then we had an invalid source anyhow! */
- invalid_sources=1;
- }
- }
- }
+
+ /*---------------------- Sanity Checking ------------------- */
+
+ if (ElementStatus->DataTransferElementCount == 0)
+ FatalError("no Data Transfer Element reported\n");
+
+ if (ElementStatus->StorageElementCount == 0)
+ FatalError("no Storage Elements reported\n");
+
+
+ /* ---------------------- Reset SourceStorageElementNumbers ------- */
+
+ /*
+ * Re-write the SourceStorageElementNumber code *AGAIN*.
+ *
+ * Pass1:
+ * Translate from raw element # to our translated # (if possible).
+ * First, check the SourceStorageElementNumbers against the list of
+ * filled slots. If the slots indicated are empty, we accept that list as
+ * valid. Otherwise decide the SourceStorageElementNumbers are invalid.
+ *
+ * Pass2:
+ * If we had some invalid (or unknown) SourceStorageElementNumbers
+ * then we must search for free slots, and assign SourceStorageElementNumbers
+ * to those free slots. We happen to already built a list of free
+ * slots as part of the process of reading the storage element numbers
+ * from the tape. So that's easy enough to do!
+ */
#ifdef DEBUG_TAPELIST
- fprintf(stderr,"invalid_sources=%d\n",invalid_sources);
+ fprintf(stderr, "empty slots: %d\n", EmptyStorageElementCount);
+ if (EmptyStorageElementCount)
+ {
+ for (i = 0; i < EmptyStorageElementCount; i++)
+ {
+ fprintf(stderr, "empty: %d\n", EmptyStorageElementAddress[i]);
+ }
+ }
#endif
- /* Pass2: */
- /* okay, we have invalid sources, so let's see what they should be: */
- /* Note: If EmptyStorageElementCount is < # of drives, the leftover
- * drives will be assigned a -1 (see the initialization loop for
- * EmptyStorageElementAddress above), which will be reported as "slot 0"
- * by the user interface. This is an invalid value, but more useful for us
- * to have than just crapping out here :-(.
- */
- if (invalid_sources) {
- empty_idx=0;
- for (i=0;i<ElementStatus->DataTransferElementCount;i++) {
- if (ElementStatus->DataTransferElementFull[i]) {
+ /*
+ * Now we re-assign origin slots if the "real" origin slot
+ * is obviously defective:
+ */
+ /* pass one: */
+ for (i = 0; i < ElementStatus->DataTransferElementCount; i++)
+ {
+ int elnum;
+
+ /* if we have an element, then ... */
+ if (ElementStatus->DataTransferElementFull[i])
+ {
+ elnum = ElementStatus->DataTransferElementSourceStorageElementNumber[i];
+ /* if we have an element number, then ... */
+ if (elnum >= 0)
+ {
+ /* Now to translate the elnum: */
+ ElementStatus->DataTransferElementSourceStorageElementNumber[i] = -1;
+ for (j = 0; j < ElementStatus->StorageElementCount; j++)
+ {
+ if (elnum == ElementStatus->StorageElementAddress[j])
+ {
+ /* now see if the element # is already occupied:*/
+ if (!ElementStatus->StorageElementFull[j])
+ {
+ /* properly set the source... */
+ ElementStatus->DataTransferElementSourceStorageElementNumber[i] = j;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Pass2: */
+ /* We have invalid sources, so let's see what they should be: */
+ /* Note: If EmptyStorageElementCount is < # of drives, the leftover
+ * drives will be assigned a -1 (see the initialization loop for
+ * EmptyStorageElementAddress above), which will be reported as "slot 0"
+ * by the user interface. This is an invalid value, but more useful for us
+ * to have than just crapping out here :-(.
+ */
+ empty_idx=0;
+ for (i = 0; i < ElementStatus->DataTransferElementCount; i++)
+ {
+ if (ElementStatus->DataTransferElementFull[i] &&
+ ElementStatus->DataTransferElementSourceStorageElementNumber[i] < 0)
+ {
#ifdef DEBUG_TAPELIST
- fprintf(stderr,"for drive %d, changing source %d to %d (empty slot #%d)\n",
- i,
- ElementStatus->DataTransferElementSourceStorageElementNumber[i],
- EmptyStorageElementAddress[empty_idx],
- empty_idx);
+ fprintf(stderr,"for drive %d, changing to %d (empty slot #%d)\n",
+ i,
+ EmptyStorageElementAddress[empty_idx],
+ empty_idx);
#endif
-
- ElementStatus->DataTransferElementSourceStorageElementNumber[i]=
- EmptyStorageElementAddress[empty_idx++];
- }
- }
- }
-
- /* and done! */
- free(mode_sense);
- free(EmptyStorageElementAddress);
- return ElementStatus;
-
+ ElementStatus->DataTransferElementSourceStorageElementNumber[i] =
+ EmptyStorageElementAddress[empty_idx++];
+ }
+ }
+
+ /* and done! */
+ free(mode_sense);
+ free(EmptyStorageElementAddress);
+ return ElementStatus;
}
/*************************************************************************/
+RequestSense_T *PositionElement(DEVICE_TYPE MediumChangerFD,
+ int DestinationAddress,
+ ElementStatus_T *ElementStatus)
+{
+ RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
+ CDB_T CDB;
+
+ CDB[0] = 0x2b;
+ CDB[1] = 0;
+ CDB[2] = (unsigned char)(ElementStatus->TransportElementAddress >> 8);
+ CDB[3] = (unsigned char)ElementStatus->TransportElementAddress;
+ CDB[4] = (unsigned char)(DestinationAddress >> 8);
+ CDB[5] = (unsigned char)DestinationAddress;
+ CDB[6] = 0;
+ CDB[7] = 0;
+ CDB[8] = 0;
+ CDB[9] = 0;
+
+ if(SCSI_ExecuteCommand( MediumChangerFD, Output, &CDB, 10,
+ NULL, 0, RequestSense) != 0)
+ {
+ return RequestSense;
+ }
+ free(RequestSense);
+ return NULL; /* success */
+}
/* Now the actual media movement routine! */
-RequestSense_T *MoveMedium(DEVICE_TYPE MediumChangerFD, int SourceAddress,
- int DestinationAddress,
- ElementStatus_T *ElementStatus,
- Inquiry_T *inquiry_info, SCSI_Flags_T *flags)
+RequestSense_T *MoveMedium( DEVICE_TYPE MediumChangerFD, int SourceAddress,
+ int DestinationAddress,
+ ElementStatus_T *ElementStatus,
+ Inquiry_T *inquiry_info, SCSI_Flags_T *flags)
+{
+ RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
+ CDB_T CDB;
+
+ if (inquiry_info->MChngr && inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)
+ {
+ /* if using the ATTACHED API */
+ CDB[0] = 0xA7; /* MOVE_MEDIUM_ATTACHED */
+ }
+ else
+ {
+ CDB[0] = 0xA5; /* MOVE MEDIUM */
+ }
+
+ CDB[1] = 0; /* Reserved */
+
+ /* Transport Element Address */
+ CDB[2] = (unsigned char)(ElementStatus->TransportElementAddress >> 8);
+ CDB[3] = (unsigned char)ElementStatus->TransportElementAddress;
+
+ /* Source Address */
+ CDB[4] = (unsigned char)(SourceAddress >> 8);
+ CDB[5] = (unsigned char)SourceAddress;
+
+ /* Destination Address */
+ CDB[6] = (unsigned char)(DestinationAddress >> 8);
+ CDB[7] = (unsigned char)DestinationAddress;
+
+ CDB[8] = 0; /* Reserved */
+ CDB[9] = 0; /* Reserved */
+
+ if (flags->invert)
+ {
+ CDB[10] = 1; /* Reserved */
+ }
+ else
+ {
+ CDB[10] = 0;
+ }
+ /* eepos controls the tray for import/export elements, sometimes. */
+ CDB[11] = flags->eepos << 6; /* Control */
+
+ if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 12,
+ NULL, 0, RequestSense) != 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Move Medium (0x%02X) failed\n", CDB[0]);
+#endif
+ return RequestSense;
+ }
+
+ free(RequestSense);
+ return NULL; /* success! */
+}
+
+
+/* Now the actual Exchange Medium routine! */
+RequestSense_T *ExchangeMedium( DEVICE_TYPE MediumChangerFD, int SourceAddress,
+ int DestinationAddress, int Dest2Address,
+ ElementStatus_T *ElementStatus,
+ SCSI_Flags_T *flags)
{
- RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
- CDB_T CDB;
- if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) { /* if using the ATTACHED API */
- CDB[0]=0xA7; /* MOVE_MEDIUM_ATTACHED */
- } else {
- CDB[0] = 0xA5; /* MOVE MEDIUM */
- }
- CDB[1] = 0; /* Reserved */
- CDB[2] = (ElementStatus->TransportElementAddress >> 8) & 0xFF; /* Transport Element Address MSB */
- CDB[3] = (ElementStatus->TransportElementAddress) & 0xff; /* Transport Element Address LSB */
- CDB[4] = (SourceAddress >> 8) & 0xFF; /* Source Address MSB */
- CDB[5] = SourceAddress & 0xFF; /* Source Address MSB */
- CDB[6] = (DestinationAddress >> 8) & 0xFF; /* Destination Address MSB */
- CDB[7] = DestinationAddress & 0xFF; /* Destination Address MSB */
- CDB[8] = 0; /* Reserved */
- CDB[9] = 0; /* Reserved */
- if (flags->invert) {
- CDB[10] = 1; /* Reserved */
- } else {
- CDB[10] = 0;
- }
- /* eepos controls the tray for import/export elements, sometimes. */
- CDB[11] = 0 | (flags->eepos <<6); /* Control */
-
- if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 12,
- NULL, 0, RequestSense) != 0) {
- return RequestSense;
- }
- free(RequestSense);
- return NULL; /* success! */
+ RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
+ CDB_T CDB;
+
+ CDB[0] = 0xA6; /* EXCHANGE MEDIUM */
+ CDB[1] = 0; /* Reserved */
+
+ /* Transport Element Address */
+ CDB[2] = (unsigned char)(ElementStatus->TransportElementAddress >> 8);
+ CDB[3] = (unsigned char)ElementStatus->TransportElementAddress;
+
+ /* Source Address */
+ CDB[4] = (unsigned char)(SourceAddress >> 8);
+ CDB[5] = (unsigned char)SourceAddress;
+
+ /* Destination Address */
+ CDB[6] = (unsigned char)(DestinationAddress >> 8);
+ CDB[7] = (unsigned char)DestinationAddress;
+
+ /* move destination back to source? */
+ CDB[8] = (unsigned char)(Dest2Address >> 8);
+ CDB[9] = (unsigned char)Dest2Address;
+ CDB[10] = 0;
+
+ if (flags->invert)
+ {
+ CDB[10] |= 2; /* INV2 */
+ }
+
+ if (flags->invert2)
+ {
+ CDB[1] |= 1; /* INV1 */
+ }
+
+ /* eepos controls the tray for import/export elements, sometimes. */
+ CDB[11] = flags->eepos << 6; /* Control */
+
+#ifdef DEBUG_EXCHANGE
+ dump_cdb(&CDB,12);
+#endif
+
+ if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 12,
+ NULL, 0, RequestSense) != 0)
+ {
+ return RequestSense;
+ }
+ free(RequestSense);
+ return NULL; /* success! */
}
-/* for Linux, this creates a way to do a short erase... the @#$%@ st.c
+
+/*
+ * for Linux, this creates a way to do a short erase... the @#$%@ st.c
* driver defaults to doing a long erase!
*/
-RequestSense_T *Erase(DEVICE_TYPE MediumChangerFD) {
- RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
- CDB_T CDB;
-
- CDB[0]=0x19;
- CDB[1]=0; /* Short! */
- CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
-
- if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6,
- NULL, 0, RequestSense) != 0) {
- return RequestSense;
- }
- free(RequestSense);
- return NULL; /* Success! */
-}
-
-
-#ifdef LONG_PRINT_REQUEST_SENSE
-
-static char *sense_keys[] = {
- "No Sense", /* 00 */
- "Recovered Error", /* 01 */
- "Not Ready", /* 02 */
- "Medium Error", /* 03 */
- "Hardware Error", /* 04 */
- "Illegal Request", /* 05 */
- "Unit Attention", /* 06 */
- "Data Protect", /* 07 */
- "Blank Check", /* 08 */
- "0x09", /* 09 */
- "0x0a", /* 0a */
- "Aborted Command", /* 0b */
- "0x0c", /* 0c */
- "Volume Overflow", /* 0d */
- "Miscompare", /* 0e */
- "0x0f" /* 0f */
-};
+RequestSense_T *Erase(DEVICE_TYPE MediumChangerFD)
+{
+ RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
+ CDB_T CDB;
-static char Yes[]="yes";
-static char No[]="no";
+ CDB[0] = 0x19;
+ CDB[1] = 0; /* Short! */
+ CDB[2] = CDB[3] = CDB[4] = CDB[5] = 0;
-void PrintRequestSense(RequestSense_T *RequestSense)
-{
- char *msg;
-
- fprintf(stderr, "mtx: Request Sense: Long Report=yes\n");
- fprintf(stderr, "mtx: Request Sense: Valid Residual=%s\n", RequestSense->Valid ? Yes : No);
- if (RequestSense->ErrorCode==0x70) {
- msg = "Current" ;
- } else if (RequestSense->ErrorCode==0x71) {
- msg = "Deferred" ;
- } else {
- msg = "Unknown?!" ;
- }
- fprintf(stderr, "mtx: Request Sense: Error Code=%0x (%s)\n",RequestSense->ErrorCode,msg);
- fprintf(stderr, "mtx: Request Sense: Sense Key=%s\n",sense_keys[RequestSense->SenseKey]);
- fprintf(stderr, "mtx: Request Sense: FileMark=%s\n", RequestSense->Filemark ? Yes : No);
- fprintf(stderr, "mtx: Request Sense: EOM=%s\n", RequestSense->EOM ? Yes : No);
- fprintf(stderr, "mtx: Request Sense: ILI=%s\n", RequestSense->ILI ? Yes : No);
- if (RequestSense->Valid) {
- fprintf(stderr, "mtx: Request Sense: Residual = %02X %02X %02X %02X\n",RequestSense->Information[0],RequestSense->Information[1],RequestSense->Information[2],RequestSense->Information[3]);
- }
- fprintf(stderr,"mtx: Request Sense: Additional Sense Code = %02X\n", RequestSense->AdditionalSenseCode);
- fprintf(stderr,"mtx: Request Sense: Additional Sense Qualifier = %02X\n", RequestSense->AdditionalSenseCodeQualifier);
- if (RequestSense->SKSV) {
- fprintf(stderr,"mtx: Request Sense: Field in Error = %02X\n",RequestSense->BitPointer);
- }
- fprintf(stderr,"mtx: Request Sense: BPV=%s\n",RequestSense->BPV ? Yes : No);
- fprintf(stderr,"mtx: Request Sense: Error in CDB=%s\n",RequestSense->CommandData ? Yes : No);
- fprintf(stderr,"mtx: Request Sense: SKSV=%s\n",RequestSense->SKSV ? Yes : No);
- fflush(stderr);
- if (RequestSense->BPV || RequestSense -> SKSV) {
- fprintf(stderr,"mtx: Request Sense: Field Pointer = %02X %02X\n",RequestSense->FieldData[0],RequestSense->FieldData[1]);
- }
+ if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6, NULL, 0, RequestSense) != 0)
+ {
+ /* If error is UNIT ATTENTION then retry the request */
+ if (RequestSense->ErrorCode != 0x70 || RequestSense->SenseKey != 6 ||
+ ClearUnitAttention(MediumChangerFD, RequestSense) != 0 ||
+ SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6, NULL, 0, RequestSense) != 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Erase (0x19) failed\n");
+#endif
+ return RequestSense;
+ }
+ }
+
+ free(RequestSense);
+ return NULL; /* Success! */
}
+/* Routine to send an LOAD/UNLOAD from the MMC/SSC spec to a device.
+ * For tapes and changers this can be used either to eject a tape
+ * or to eject a magazine (on some Seagate changers, when sent to LUN 1 ).
+ * For CD/DVDs this is used to Load or Unload a disc which is required by
+ * some media changers.
+ */
-#else
-void PrintRequestSense(RequestSense_T *RequestSense)
+int LoadUnload(DEVICE_TYPE fd, int bLoad)
{
- int i;
- fprintf(stderr, "mtx: Request Sense: %02X",
- ((unsigned char *) RequestSense)[0]);
- for (i = 1; i < sizeof(RequestSense_T); i++)
- fprintf(stderr, " %02X", ((unsigned char *) RequestSense)[i]);
- fprintf(stderr, "\n");
+ CDB_T CDB;
+ /* okay, now for the command: */
+
+ CDB[0] = 0x1B;
+ CDB[4] = bLoad ? 3 : 2;
+ CDB[1] = CDB[2] = CDB[3] = CDB[5] = 0;
+
+ if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, NULL, 0, &scsi_error_sense) != 0)
+ {
+ /* If error is UNIT ATTENTION then retry the request */
+ if (scsi_error_sense.ErrorCode != 0x70 || scsi_error_sense.SenseKey != 6 ||
+ ClearUnitAttention(fd, &scsi_error_sense) != 0 ||
+ SCSI_ExecuteCommand(fd, Input, &CDB, 6, NULL, 0, &scsi_error_sense) != 0)
+ {
+ PrintRequestSense(&scsi_error_sense);
+ fprintf(stderr, "Eject (0x1B) failed\n");
+ return -1; /* could not do! */
+ }
+ }
+ return 0; /* did do! */
}
-#endif
+/* Routine to send an START/STOP from the MMC/SSC spec to a device.
+ * For tape drives this may be required prior to using the changer
+ * Load or Unload commands.
+ * For CD/DVD drives this is used to Load or Unload a disc which may be
+ * required by some media changers.
+ */
+
+int StartStop(DEVICE_TYPE fd, int bStart)
+{
+ CDB_T CDB;
+ /* okay, now for the command: */
+
+ CDB[0] = 0x1B;
+ CDB[4] = bStart ? 1 : 0;
+ CDB[1] = CDB[2] = CDB[3] = CDB[5] = 0;
+
+ if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,NULL, 0, &scsi_error_sense) != 0)
+ {
+ PrintRequestSense(&scsi_error_sense);
+ fprintf(stderr, "Eject (0x1B) failed\n");
+ return -1; /* could not do! */
+ }
+ return 0; /* did do! */
+}
-/* $Date: 2002/02/05 16:51:11 $
- * $Log: mtxl.c,v $
- * Revision 1.8.2.3 2002/02/05 16:51:11 elgreen
- * mtx 1.2.16pre3
- *
- * Revision 1.8.2.2 2002/01/22 16:27:47 elgreen
- * Handle too-big transport element descriptor lengths
- *
- * Revision 1.8.2.1 2002/01/17 22:24:35 elgreen
- * Handle ADIC silliness of saying that it has 1 import/export element whose
- * descriptor is 0 bytes in length
- *
- * Revision 1.8 2001/06/25 23:06:22 elgreen
- * Readying this for 1.2.13 release
- *
- * Revision 1.7 2001/06/25 04:56:35 elgreen
- * Kai to the rescue *again* :-)
- *
- * Revision 1.6 2001/06/24 07:02:01 elgreen
- * Kai's fix was better than mine.
- *
- * Revision 1.5 2001/06/24 06:59:19 elgreen
- * Kai found bug in the barcode backoff.
- *
- * Revision 1.4 2001/06/15 18:56:54 elgreen
- * Arg, it doesn't help to check for 0 robot arms if you force it to 1!
- *
- * Revision 1.3 2001/06/15 14:26:09 elgreen
- * Fixed brain-dead case of HP loaders that report they have no robot arms.
- *
- * Revision 1.2 2001/06/09 17:26:26 elgreen
- * Added 'nobarcode' command to mtx (to skip the initial request asking for
- * barcodes for mtx status purposes).
- *
- * Revision 1.1.1.1 2001/06/05 17:10:25 elgreen
- * Initial import into SourceForge
- *
- * Revision 1.29 2001/05/01 01:39:23 eric
- * Remove the Exabyte special case code, which seemed to be barfing me :-(.
- *
- * Revision 1.28 2001/04/28 00:03:10 eric
- * update for 1.2.12.
- *
- * Revision 1.27 2001/04/18 19:27:39 eric
- * whoops, move ImportExport parser into the $#%!@ ImportExport test :-(.
- *
- * Revision 1.26 2001/04/18 18:55:44 eric
- * turn too many storage elements into a warning rather than a fatal error (sigh!)
- *
- * Revision 1.25 2001/04/18 18:46:54 eric
- * $%!@# "C" precedence rules.
- *
- * Revision 1.24 2001/04/18 17:58:04 eric
- * initialize EmptyStorageElementCount (sigh)
- *
- * Revision 1.23 2001/04/18 16:55:03 eric
- * hopefully get the initialization for element status straigtened out.
- *
- * Revision 1.22 2001/04/18 16:32:59 eric
- * Cleaned up all -Wall messages.
- *
- * Revision 1.21 2001/04/18 16:08:08 eric
- * after re-arranging & fixing bugs
- *
- * Revision 1.20 2001/04/17 21:28:43 eric
- * mtx 1.2.11
- *
- * Revision 1.19 2001/04/02 20:12:48 eric
- * checkin, mtx 1.2.11pre6
- *
- * Revision 1.18 2001/03/06 01:37:05 eric
- * Solaris changes
- *
- * Revision 1.17 2001/02/26 20:56:12 eric
- * added debugging, removed Makefile
- *
- * Revision 1.16 2001/02/19 23:22:28 eric
- * Add E2 bits
- *
- * Revision 1.15 2001/02/19 23:02:04 eric
- * Apply SPARC patch.
- *
- * Revision 1.14 2001/01/26 15:58:10 eric
- * Make work on solaris?
- *
- * Revision 1.13 2001/01/17 19:39:01 eric
- * Changed mtx.h and mtxl.c to use autoconf stuff to figure out which SCSI
- * routines to use (this way, if we ever port to an OS that has an identical
- * SCSI interface to one of the supported OS's, we don't have to do anything
- * special!).
- *
- * Revision 1.12 2001/01/17 16:36:26 eric
- * added date & revision strings as needed
- *
- * Revision 1.11 2000/11/30 20:35:18 eric
- * Added Erase command.
- *
+/* Routine to send a LOCK/UNLOCK from the SSC/MMC spec to a device.
+ * This can be used to prevent or allow the Tape or CD/DVD from being
+ * removed.
*/
+
+int LockUnlock(DEVICE_TYPE fd, int bLock)
+{
+ CDB_T CDB;
+ /* okay, now for the command: */
+
+ CDB[0] = 0x1E;
+ CDB[1] = CDB[2] = CDB[3] = CDB[5] = 0;
+ CDB[4] = (char)bLock;
+
+ if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, NULL, 0, &scsi_error_sense) != 0)
+ {
+ PrintRequestSense(&scsi_error_sense);
+ fprintf(stderr, "Lock/Unlock (0x1E) failed\n");
+ return -1; /* could not do! */
+ }
+ return 0; /* did do! */
+}
+
+int ClearUnitAttention(DEVICE_TYPE fd, RequestSense_T *RequestSense)
+{
+ CDB_T CDB;
+ int RetryCount = 10; /* Unit Attentions may be stacked */
+ RequestSense_T unit_attention_sense;
+
+ CDB[0] = 0x03; /* Request Sense */
+ CDB[4] = (char)sizeof(*RequestSense);
+ CDB[1] = CDB[2] = CDB[3] = CDB[5] = 0;
+
+ while (RetryCount-- > 0)
+ {
+ if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,
+ &unit_attention_sense, sizeof(unit_attention_sense),
+ RequestSense) != 0)
+ {
+ fprintf(stderr, "RequestSense (0x03) failed\n");
+ return -1; /* could not do! */
+ }
+
+ if (unit_attention_sense.SenseKey == 0)
+ {
+ /* If SenseKey is NO SENSE then we are done. */
+ return 0;
+ }
+ }
+ return -1; /* did do! */
+
+}
+
+static char Spaces[] = " ";
+
+void PrintHex(int Indent, unsigned char *Buffer, int Length)
+{
+ int idxBuffer;
+ int idxAscii;
+ int PadLength;
+ char cAscii;
+
+ for (idxBuffer = 0; idxBuffer < Length; idxBuffer++)
+ {
+ if ((idxBuffer % 16) == 0)
+ {
+ if (idxBuffer > 0)
+ {
+ fputc('\'', stderr);
+
+ for (idxAscii = idxBuffer - 16; idxAscii < idxBuffer; idxAscii++)
+ {
+ cAscii = Buffer[idxAscii] >= 0x20 && Buffer[idxAscii] < 0x7F ? Buffer[idxAscii] : '.';
+ fputc(cAscii, stderr);
+ }
+ fputs("'\n", stderr);
+ }
+ fprintf(stderr, "%.*s%04X: ", Indent, Spaces, idxBuffer);
+ }
+ fprintf(stderr, "%02X ", (unsigned char)Buffer[idxBuffer]);
+ }
+
+ PadLength = 16 - (Length % 16);
+
+ if (PadLength > 0)
+ {
+ fprintf(stderr, "%.*s'", 3 * PadLength, Spaces);
+
+ for (idxAscii = idxBuffer - (16 - PadLength); idxAscii < idxBuffer; idxAscii++)
+ {
+ cAscii = Buffer[idxAscii] >= 0x20 && Buffer[idxAscii] < 0x80 ? Buffer[idxAscii] : '.';
+ fputc(cAscii, stderr);
+ }
+ fputs("'\n", stderr);
+ }
+
+ fflush(stderr);
+}
+
+static char *sense_keys[] =
+{
+ "No Sense", /* 00 */
+ "Recovered Error", /* 01 */
+ "Not Ready", /* 02 */
+ "Medium Error", /* 03 */
+ "Hardware Error", /* 04 */
+ "Illegal Request", /* 05 */
+ "Unit Attention", /* 06 */
+ "Data Protect", /* 07 */
+ "Blank Check", /* 08 */
+ "0x09", /* 09 */
+ "0x0a", /* 0a */
+ "Aborted Command", /* 0b */
+ "0x0c", /* 0c */
+ "Volume Overflow", /* 0d */
+ "Miscompare", /* 0e */
+ "0x0f" /* 0f */
+};
+
+static char Yes[] = "yes";
+static char No[] = "no";
+
+void PrintRequestSense(RequestSense_T *RequestSense)
+{
+ char *msg;
+
+ fprintf(stderr, "mtx: Request Sense: Long Report=yes\n");
+ fprintf(stderr, "mtx: Request Sense: Valid Residual=%s\n", RequestSense->Valid ? Yes : No);
+
+ if (RequestSense->ErrorCode == 0x70)
+ {
+ msg = "Current" ;
+ }
+ else if (RequestSense->ErrorCode == 0x71)
+ {
+ msg = "Deferred" ;
+ }
+ else
+ {
+ msg = "Unknown?!" ;
+ }
+
+ fprintf(stderr, "mtx: Request Sense: Error Code=%0x (%s)\n", RequestSense->ErrorCode, msg);
+ fprintf(stderr, "mtx: Request Sense: Sense Key=%s\n", sense_keys[RequestSense->SenseKey]);
+ fprintf(stderr, "mtx: Request Sense: FileMark=%s\n", RequestSense->Filemark ? Yes : No);
+ fprintf(stderr, "mtx: Request Sense: EOM=%s\n", RequestSense->EOM ? Yes : No);
+ fprintf(stderr, "mtx: Request Sense: ILI=%s\n", RequestSense->ILI ? Yes : No);
+
+ if (RequestSense->Valid)
+ {
+ fprintf(stderr, "mtx: Request Sense: Residual = %02X %02X %02X %02X\n",RequestSense->Information[0],RequestSense->Information[1],RequestSense->Information[2],RequestSense->Information[3]);
+ }
+
+ fprintf(stderr,"mtx: Request Sense: Additional Sense Code = %02X\n", RequestSense->AdditionalSenseCode);
+ fprintf(stderr,"mtx: Request Sense: Additional Sense Qualifier = %02X\n", RequestSense->AdditionalSenseCodeQualifier);
+
+ if (RequestSense->SKSV)
+ {
+ fprintf(stderr,"mtx: Request Sense: Field in Error = %02X\n", RequestSense->BitPointer);
+ }
+
+ fprintf(stderr, "mtx: Request Sense: BPV=%s\n", RequestSense->BPV ? Yes : No);
+ fprintf(stderr, "mtx: Request Sense: Error in CDB=%s\n", RequestSense->CommandData ? Yes : No);
+ fprintf(stderr, "mtx: Request Sense: SKSV=%s\n", RequestSense->SKSV ? Yes : No);
+
+ if (RequestSense->BPV || RequestSense -> SKSV)
+ {
+ fprintf(stderr, "mtx: Request Sense: Field Pointer = %02X %02X\n",
+ RequestSense->FieldData[0], RequestSense->FieldData[1]);
+ }
+
+ fflush(stderr);
+}