From 3fafa033133e3f4d44399072fbe1926880e91cd8 Mon Sep 17 00:00:00 2001 From: Alexander Moibenko <41128752+moibenko@users.noreply.github.com> Date: Fri, 8 Nov 2019 10:29:18 -0600 Subject: [PATCH] Delete mtxl.c --- mtx-1.3.12/mtxl.c | 2027 --------------------------------------------- 1 file changed, 2027 deletions(-) delete mode 100644 mtx-1.3.12/mtxl.c diff --git a/mtx-1.3.12/mtxl.c b/mtx-1.3.12/mtxl.c deleted file mode 100644 index b7741e7..0000000 --- a/mtx-1.3.12/mtxl.c +++ /dev/null @@ -1,2027 +0,0 @@ -/* MTX -- SCSI Tape Attached Medium Changer Control Program - - Copyright 1997-1998 by Leonard N. Zubkoff. - Copyright 1999-2006 by Eric Lee Green. - Copyright 2007-2008 by Robert Nelson - - $Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $ - $Revision: 193 $ - - This file created Feb 2000 by Eric Lee Green 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 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@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_BARCODE */ -/* #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 */ - -/* only so many element per SCSI Read Element Status */ -#define SCSI_RES_ELEMENTS 5000 - -/* First, do some SCSI routines: */ - -/* the camlib is used on FreeBSD. */ -#if HAVE_CAMLIB_H -# include "scsi_freebsd.c" -#endif - -/* the scsi_ctl interface is used on HP/UX. */ -#if HAVE_SYS_SCSI_CTL_H -# include "scsi_hpux.c" -#endif - -/* the 'sg' interface is used on Linux. */ -#if HAVE_SCSI_SG_H -# 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" -#endif - -/* 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" -#endif - -/* Hmm, dunno what to do about Digital Unix at the moment. */ -#ifdef DIGITAL_UNIX -# include "du/scsi.c" -#endif - -#ifdef VMS -# 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) - { -#ifdef DEBUG - fprintf(stderr, "SCSI Inquiry Command failed\n"); -#endif - free(Inquiry); - return NULL; /* sorry! */ - } - return Inquiry; -} - - -#if defined(DEBUG_NSM) || defined(DEBUG_EXCHANGE) -/* DEBUG */ -static void dump_cdb(unsigned char *CDB, int len) -{ - fprintf(stderr,"CDB:"); - PrintHex(1, CDB, len); -} -#endif - - -#if defined(DEBUG_NSM) || defined(DEBUG_ADIC) -/* DEBUG */ -static void dump_data(unsigned char *data, int len) -{ - 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; - 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; - } - } - - *TargetPointer = '\0'; /* works even if we had to break from above... */ - vfprintf(stderr, FormatBuffer, ArgumentPointer); - va_end(ArgumentPointer); - -#ifndef VMS - //exit(1); - return; /* if used as library routine it can not exit, because this would terminate the caller */ -#else - 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; - } -} - -/* 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; -} - -/* malloc some memory, zeroing it too: */ -void *xzmalloc(size_t Size) -{ - void *Result = (void *)xmalloc(Size); - - slow_bzero(Result, Size); - return Result; -} - - -int min(int x, int y) -{ - return (x < y ? x : y); -} - - -int max(int x, int 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; - - /* 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,&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. */ - - /* 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 - PrintHex(0, input_buffer, 30); -#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)); - - /* 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; - - /* 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; - } -#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); -#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)); - -#ifdef IMPORT_EXPORT_HACK - 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); -#endif - - return retval; -} - -void FreeElementData(ElementStatus_T *data) -{ - if (!data) - { - return; - } - free(data->DataTransferElementAddress); - free(data->DataTransferElementSourceStorageElementNumber); - free(data->DataTransferElementPhysicalLocation); - free(data->DataTransferElementProductId); - free(data->DataTransferElementSerialNumber); - free(data->StorageElementPhysicalLocation); - 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->DataTransferElementPhysicalLocation = - (int *)xzmalloc(sizeof(int) * (mode_sense->NumDataTransfer + 1)); - retval->DataTransferElementProductId = - (serialnumber *)xzmalloc(sizeof(serialnumber) * (mode_sense->NumDataTransfer + 1)); - retval->DataTransferElementSerialNumber = - (serialnumber *)xzmalloc(sizeof(serialnumber) * (mode_sense->NumDataTransfer + 1)); - retval->StorageElementPhysicalLocation = - (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumStorage + 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 < 36; i++) - { - *dest = *src++; - - if ((*dest < 32) || (*dest > 127)) - { - *dest = '\0'; - } - - dest++; - } - *dest = 0; /* null-terminate */ -} - -void copy_physical_location(unsigned char *src, unsigned char *dest) -{ - while ((*src< 32) || (*src > 127)) { - src++; - } - strcpy((char *)dest, (char *)src); -} - -void copy_char_buffer(unsigned char *src, unsigned char *dest, int num) -{ - int i; - while ((*src< 32) || (*src > 127)) { - src++; - } - for (i=0; i < num; i++) - { - *dest = *src++; - - if ((*dest < 32) || (*dest > 127)) - { - *dest = 0; - break; - } - dest++; - } - *dest = 0; -} - -/* This #%!@# routine has more parameters than I can count! */ -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; - - unsigned char *DataBuffer; /* size of data... */ - -#ifdef HAVE_GET_ID_LUN - 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; - } - - DataBuffer = (unsigned char *)xzmalloc(NumBytes + 1); - - slow_bzero((char *)RequestSense, sizeof(RequestSense_T)); - -#ifdef HAVE_GET_ID_LUN - scsi_id = SCSI_GetIDLun(MediumChangerFD); -#endif - - 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); -#else - /* Element Type Code = 0, VolTag = 1 */ - CDB[1] = (unsigned char)((flags->no_barcodes ? 0 : 0x10) | flags->elementtype); -#endif - /* 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 */ - if (((flags->elementtype == DataTransferElement || flags->elementtype == StorageElement)) && (flags->absolute_addressing == 1)) { - CDB[6] = 0x01; /* set DVCID to read physical location */ - } - - /* 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) - { - -#ifdef DEBUG - fprintf(stderr, "Read Element Status (0x%02X) failed\n", CDB[0]); -#endif - - /* - 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[1] &= ~0x10; /* clear bar code flag! */ - -#ifdef DEBUG_BARCODE - fprintf(stderr,"CDB:\n"); - PrintHex(2, CDB, 12); -#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 :-(. */ - fprintf(stderr,"Data:\n"); - PrintHex(2, DataBuffer, 100); -#endif - 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, - int *pNextElement, - boolean absolute_address - ) -{ - 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); -#endif - - 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++; -#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) - { -#ifdef DEBUG - fprintf(stderr,"boom! ElementTypeCode=%d\n",ElementStatusPage->ElementTypeCode); -#endif - FatalError("Transport Element Descriptor Length too short: %d\n", TransportElementDescriptorLength); - } - } -#ifdef DEBUG - fprintf(stderr,"Transport Element Descriptor Length=%d\n",TransportElementDescriptorLength); -#endif - BytesAvailable = - BigEndian24(ElementStatusPage->ByteCountOfDescriptorDataAvailable); -#ifdef DEBUG - fprintf(stderr,"%d bytes of descriptor data available in descriptor\n", - BytesAvailable); -#endif - /* 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,"TransportElementAddress=%d\n",ElementStatus->TransportElementAddress); -#endif - break; - - /* we treat ImportExport elements as if they were normal - * storage elements now, sigh... - */ - case ImportExportElement: -#ifdef DEBUG - fprintf(stderr,"ImportExportElement=%d\n",ElementStatus->StorageElementCount); -#endif - 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,"StorageElementCount=%d ElementAddress = %d ",ElementStatus->StorageElementCount,BigEndian16(TransportElementDescriptor->ElementAddress)); -#endif - /* 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]; */ - } - 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. */ - } - - if (absolute_address){ - copy_physical_location(TransportElementDescriptor->AlternateVolumeTag, ElementStatus->StorageElementPhysicalLocation[ElementStatus->StorageElementCount]); - } - - 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; - */ - - if (absolute_address){ - ElementStatus->DataTransferElementPhysicalLocation[ElementStatus->DataTransferElementCount] = - BigEndian16(TransportElementDescriptor->SourceStorageElementAddress); - InquiryShort_T *inqs; - inqs = (InquiryShort_T *) TransportElementDescriptor->PrimaryVolumeTag; - copy_char_buffer(inqs->SerialNumber, ElementStatus->DataTransferElementSerialNumber[ElementStatus->DataTransferElementCount], 12); - copy_char_buffer(inqs->ProductIdentification+2, ElementStatus->DataTransferElementProductId[ElementStatus->DataTransferElementCount], 12); - ElementStatus->DataTransferElementCount++; - break; - } - ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount] = - BigEndian16(TransportElementDescriptor->ElementAddress); - ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount] = - TransportElementDescriptor->Full; - ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount] = - BigEndian16(TransportElementDescriptor->SourceStorageElementAddress); - -#ifdef 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); - } - } - } - -#ifdef DEBUG - if (pNextElement) - fprintf(stderr,"Next start element will be %d\n",*pNextElement); -#endif -} - - -/********************* Real ReadElementStatus ********************* */ - -/* - * We no longer do the funky trick to figure out ALLOCATION_LENGTH. - * Instead, we use the SCSI Generic command rather than SEND_SCSI_COMMAND - * under Linux, which gets around the @#%@ 4k buffer size in Linux. - * We still have the restriction that Linux cuts off the last two - * bytes of the SENSE DATA (Q#@$%@#$^ Linux!). Which means that the - * verbose widget won't work :-(. - - * We now look for that "attached" bit in the inquiry_info to see whether - * to use READ_ELEMENT_ATTACHED or plain old READ_ELEMENT. In addition, we - * look at the device type in the inquiry_info to see whether it is a media - * changer or tape device, and if it's a media changer device, we ignore the - * attached bit (one beta tester found an old 4-tape DAT changer that set - * the attached bit for both the tape device AND the media changer device). - -*/ - -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; - boolean is_attached = false; - int i,j; - int FirstElem, NumElements, NumThisRES; - - 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"); - fprintf(stderr,"Storage start %d, Num %d, max %d\n", mode_sense->StorageStart, mode_sense->NumStorage - mode_sense->NumImportExport, mode_sense->MaxReadElementStatusData); -#endif - flags->elementtype = StorageElement; /* sigh! */ - flags->absolute_addressing = 1; - - NumElements = mode_sense->NumStorage - mode_sense->NumImportExport; - FirstElem = mode_sense->StorageStart; - - do - { - NumThisRES = (NumElements > SCSI_RES_ELEMENTS ? SCSI_RES_ELEMENTS : NumElements); - DataBuffer = SendElementStatusRequest( MediumChangerFD, RequestSense, - inquiry_info, flags, - FirstElem, - /* adjust for import/export. */ - NumThisRES, - SCSI_RES_ELEMENTS * 52 +120); - - 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, true); - - free(DataBuffer); /* sigh! */ - FirstElem += SCSI_RES_ELEMENTS; - NumElements -= SCSI_RES_ELEMENTS; - } while ( NumElements > 0 ); - - flags->absolute_addressing = 0; - /* --------------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, - SCSI_RES_ELEMENTS * 52 +120); - 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, false); - free(DataBuffer); - ElementStatus->StorageElementCount += ElementStatus->ImportExportCount; - } - - /* ----------------- DRIVES ---------------------- */ - -#ifdef DEBUG - fprintf(stderr,"Sending request for data transfer element (drive) status\n"); -#endif - flags->elementtype = DataTransferElement; /* sigh! */ - flags->absolute_addressing = 0; - DataBuffer = SendElementStatusRequest( MediumChangerFD, RequestSense, - inquiry_info, flags, - mode_sense->DataTransferStart, - mode_sense->NumDataTransfer, - SCSI_RES_ELEMENTS * 52 +120); - 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, false); - - free(DataBuffer); /* sigh! */ - - flags->absolute_addressing = 1; - DataBuffer = SendElementStatusRequest( MediumChangerFD, RequestSense, - inquiry_info, flags, - mode_sense->DataTransferStart, - mode_sense->NumDataTransfer, - SCSI_RES_ELEMENTS * 52 +120); - 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; - } - ElementStatus->DataTransferElementCount = 0; - ParseElementStatus( EmptyStorageElementAddress, &EmptyStorageElementCount, - DataBuffer,ElementStatus, mode_sense, NULL, true); - - 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. */ - SCSI_RES_ELEMENTS * 52 +120); - 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, false); - - free(DataBuffer); - } - flags->absolute_addressing = 0; - } - 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, false); - - free(DataBuffer); /* sigh! */ - } - - ElementStatus->StorageElementCount += ElementStatus->ImportExportCount; - } - - /*---------------------- 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, "empty slots: %d\n", EmptyStorageElementCount); - if (EmptyStorageElementCount) - { - for (i = 0; i < EmptyStorageElementCount; i++) - { - fprintf(stderr, "empty: %d\n", EmptyStorageElementAddress[i]); - } - } -#endif - - /* - * 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 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; -} - -/*************************************************************************/ - -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 *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; - - 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 - * 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) - { - /* 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. - */ - -int LoadUnload(DEVICE_TYPE fd, int bLoad) -{ - 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! */ -} - -/* 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! */ -} - -/* 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! */ - -} - -/* Test unit ready: This will tell us whether the tape drive - * is currently ready to read or write. - */ - -int testUnitReady(DEVICE_TYPE fd) -{ - RequestSense_T sense; - CDB_T CDB; - unsigned char buffer[6]; - - CDB[0] = 0x00; /* TEST_UNIT_READY */ - CDB[1] = 0; - CDB[2] = 0; - CDB[3] = 0; /* 1-5 all unused. */ - CDB[4] = 0; - CDB[5] = 0; - - slow_bzero((char *)&sense,sizeof(RequestSense_T)); - return SCSI_ExecuteCommand(fd,Input,&CDB,6,buffer,0,&sense); -} - -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); -}