// ---------------------------------------------------------------------- // File: Drive/Structures.hh // Author: Eric Cano - CERN // ---------------------------------------------------------------------- /************************************************************************ * Tape Server * * Copyright (C) 2013 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * 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 more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ #pragma once #include #include #include #include #include #include #include #include "Constants.hh" #include "../Exception/Exception.hh" namespace SCSI { /** * Structures as defined in the SCSI specifications, and helper functions for them. * SPC-4 (SCSI primary commands) can be found at: * http://hackipedia.org/Hardware/SCSI/Primary%20Commands/SCSI%20Primary%20Commands%20-%204.pdf * * and SSC-3 (SCSI stream commands, i.e. tape drives) at: * http://hackipedia.org/Hardware/SCSI/Stream%20Commands/SCSI%20Stream%20Commands%20-%203.pdf */ namespace Structures { /** * Helper template to zero a structure. Small boilerplate reduction. * Should not be used with classes with virtual tables! With a zeroed * out virtual table pointer, this will be detected soon enough. * @param s pointer the struct/class. */ template void zeroStruct(C * s) { memset (s, 0, sizeof(C)); } /** * Class wrapping around Linux' SG_IO struct, providing * zeroing and automatic filling up for the mandatory structures * (cdb, databuffer, sense buffer, magic 'S', and default timeout). * Make it look like a bare sg_io_hdr_t when using & operator. * Another little boilerplate killer. */ class LinuxSGIO_t: public sg_io_hdr_t { public: LinuxSGIO_t() { zeroStruct(this); interface_id = 'S'; timeout = 30000; } template void setCDB(T * cdb) { cmdp = (unsigned char *)cdb; cmd_len = sizeof(T); } template void setSenseBuffer(T * senseBuff) throw (Tape::Exception) { if (sizeof(T) > UCHAR_MAX) throw Tape::Exception("sense structure too big in LinuxSGIO_t::setSense"); sb_len_wr = (unsigned char) sizeof(T); sbp = (unsigned char *)senseBuff; } template void setDataBuffer(T * dataBuff) { dxferp = dataBuff; dxfer_len = sizeof (T); } sg_io_hdr_t * operator & () { return (sg_io_hdr_t *) this; } }; /** * Helper function to deal with endianness. * @param t byte array in SCSI order representing a 64 bits number * @return */ inline uint64_t toU64(const unsigned char(& t)[8]) { /* Like network, SCSI is BigEndian */ return (uint64_t) ntohl ( (*(uint64_t *) t << 32) >> 32) << 32 | ntohl(*(uint64_t *) t >>32); } /** * Helper function to deal with endianness. * @param t byte array in SCSI order representing a 32 bits number * @return */ inline uint32_t toU32(const unsigned char(& t)[4]) { /* Like network, SCSI is BigEndian */ return ntohl (*((uint32_t *) t)); } /** * Helper function to deal with endianness. * for signed values * @param t byte array in SCSI order representing a 32 bits number * @return */ inline int32_t toS32(const unsigned char(& t)[4]) { /* Like network, SCSI is BigEndian */ return (int32_t)(ntohl (*((uint32_t *) t))); } /** * Helper function to deal with endianness. * @param t byte array in SCSI order representing a 16 bits number * @return */ inline uint16_t toU16(const unsigned char(& t)[2]) { /* Like network, SCSI is BigEndian */ return ntohs (*((uint16_t *) t)); } /** * Inquiry CDB as described in SPC-4. */ class inquiryCDB_t { public: inquiryCDB_t() { zeroStruct(this); opCode = SCSI::Commands::INQUIRY; } unsigned char opCode; unsigned char EVPD : 1; unsigned char : 7; unsigned char pageCode; char allocationLength[2]; unsigned char control; }; /** * Inquiry data as described in SPC-4. */ class inquiryData_t { public: inquiryData_t () { zeroStruct(this); } unsigned char perifDevType : 5; unsigned char perifQualifyer : 3; unsigned char : 7; unsigned char RMB : 1; unsigned char version : 8; unsigned char respDataFmt : 4; unsigned char HiSup : 1; unsigned char normACA : 1; unsigned char : 2; unsigned char addLength : 8; unsigned char protect : 1; unsigned char : 2; unsigned char threePC : 1; unsigned char TPGS : 2; unsigned char ACC : 1; unsigned char SCCS : 1; unsigned char addr16 : 1; unsigned char : 3; unsigned char multiP : 1; unsigned char VS1 : 1; unsigned char encServ : 1; unsigned char : 1; unsigned char VS2 : 1; unsigned char cmdQue : 1; unsigned char : 2; unsigned char sync : 1; unsigned char wbus16 : 1; unsigned char : 2; char T10Vendor[8]; char prodId[16]; char prodRevLvl[4]; char vendorSpecific1[20]; unsigned char IUS : 1; unsigned char QAS : 1; unsigned char clocking : 2; unsigned char : 4; unsigned char reserved1; unsigned char versionDescriptor[8][2]; unsigned char reserved2[22]; unsigned char vendorSpecific2[1]; }; /* * LOCATE(10) CDB as described in SSC-3. */ class locate10CDB_t { public: locate10CDB_t() { zeroStruct(this); opCode = SCSI::Commands::LOCATE_10; } // byte 0 unsigned char opCode; // OPERATION CODE (2Bh) // byte 1 unsigned char IMMED : 1; // Immediate unsigned char CP : 1; // Change Partition unsigned char BT : 1; // Block address Type unsigned char : 5; // Reserved // byte 2 unsigned char : 8; // Reserved // bytes 3-6 unsigned char logicalObjectID[4] ; // Logical object identifier or block address // byte 7 unsigned char :8; // Reserved // byte 8 unsigned char partition; // Partition // byte 9 unsigned char control; // Control byte }; /* * LOG SELECT CDB as described in SPC-4. */ class logSelectCDB_t { public: logSelectCDB_t() { zeroStruct(this); opCode = SCSI::Commands::LOG_SELECT; } // byte 0 unsigned char opCode; // OPERATION CODE (4Ch) // byte 1 unsigned char SP : 1; // the Save Parameters unsigned char PCR: 1; // the Parameter Code Reset unsigned char : 6; // Reserved // byte 2 unsigned char pageCode: 6; // PAGE CODE unsigned char PC: 2; // the Page Control // byte 3 unsigned char subPageCode; // SUBPAGE CODE (Reserved for T10000) // bytes 4-6 unsigned char reserved[3]; // Reserved // bytes 7-8 unsigned char parameterListLength[2];// PARAMETER LIST LENGTH // byte 9 unsigned char control; // CONTROL }; /** * Log sense CDB as described in SPC-4, */ class logSenseCDB_t { public: logSenseCDB_t() { zeroStruct(this); opCode = SCSI::Commands::LOG_SENSE; } unsigned char opCode; unsigned char SP : 1; unsigned char PPC: 1; unsigned char :6; unsigned char pageCode : 6; unsigned char PC : 2; unsigned char subPageCode; unsigned char reserved; unsigned char parameterPointer[2]; unsigned char allocationLength[2]; unsigned char control; }; /** * Log sense Log Page Parameter Format as described in SPC-4, */ class logSenseParameterHeader_t { public: // bytes 0-1 unsigned char parameterCode [2]; // byte 2 unsigned char formatAndLinking : 2; // reserved and List Parameter bits unsigned char TMC : 2; // Threshold Met Criteria unsigned char ETC : 1; // Enable Threshold Comparison unsigned char TSD : 1; // Target Save Disable unsigned char : 1; // DS Disable Save for T10000 unsigned char DU : 1; // Disable Update // byte 3 unsigned char parameterLength; // n-3 }; class logSenseParameter_t { public: // bytes 0-3 logSenseParameterHeader_t header; // bytes 4-n unsigned char parameterValue[1]; // parameters have variable length }; /** * Log sense Log Page Format as described in SPC-4, */ class logSenseLogPageHeader_t { public: // byte 0 unsigned char pageCode : 6; unsigned char SPF: 1; // the Subpage format unsigned char DS: 1; // the Disable Slave bit // byte 1 unsigned char subPageCode; // bytes 2-3 unsigned char pageLength[2]; // n-3 number of bytes without header }; /** * Log sense Log Page Format as described in SPC-4, */ class logSenseLogPage_t { public: // bytes 0-3 logSenseLogPageHeader_t header; // bytes 4-n logSenseParameter_t parameters [1]; // parameters have variable length }; /** * Part of a tape alert log page. * This structure does not need to be initialized, as the containing structure * (tapeAlertLogPage_t) will do it while initializing itself. */ class tapeAlertLogParameter_t { public: unsigned char parameterCode [2]; unsigned char formatAndLinking : 2; unsigned char TMC : 2; unsigned char ETC : 1; unsigned char TSD : 1; unsigned char : 1; unsigned char DU : 1; unsigned char parameterLength; unsigned char flag : 1; unsigned char : 7; }; /** * Tape alert log page, returned by LOG SENSE. Defined in SSC-3, section 8.2.3 TapeAler log page. */ template class tapeAlertLogPage_t { public: tapeAlertLogPage_t() { zeroStruct(this); } unsigned char pageCode : 6; unsigned char : 2; unsigned char subPageCode; unsigned char pageLength[2]; tapeAlertLogParameter_t parameters [n]; /** * Utility function computing the number of parameters. This converts a * length in bytes (as found in the struct) in a parameter count. * @return number of parameters. */ int parameterNumber() throw (Tape::Exception) { int numFromLength = SCSI::Structures::toU16(pageLength) / sizeof (tapeAlertLogParameter_t); return numFromLength; } }; /** * Sense buffer as defined in SPC-4, * section 4.5.2 Descriptor format sense data and * section 4.5.3 Fixed format sense data * The sense buffer size is stored in the form of * a single byte. Therefore, the constructor forbids * creation of a senseData_t structure bigger than * 255 bytes. * As the structure will be different depending on the response code, * everything after the first byte is represented by a union, which * can be any of the 2 forms (fixedFormat/descriptorFormat). * getXXXXX helper member function allow the getting of on of the other * version of the common fields. */ template class senseData_t { public: senseData_t() { if (sizeof(*this) > 255) throw Tape::Exception("In SCSI::Structures::senseData_t::senseData_t(): size too big (> 255>"); zeroStruct(this); } // byte 0 unsigned char responseCode: 7; unsigned char : 1; // Following bytes can take 2 versions: union { struct { // byte 1 unsigned char senseKey : 4; unsigned char : 4; // Additional sense code (byte 2)) unsigned char ASC; // Additional sense code qualifier (byte 3) unsigned char ASCQ; // byte 4 unsigned char : 7; unsigned char SDAT_OVFL : 1; // byte 5-6 unsigned char reserved[2]; // byte 7 unsigned char additionalSenseLength; // byte 8 onwards unsigned char additionalSenseBuffer[n - 8]; } descriptorFormat; struct { // byte 1 unsigned char obsolete; // byte 2 unsigned char senseKey : 4; unsigned char SDAT_OVFL : 1; unsigned char ILI : 1; unsigned char EOM : 1; unsigned char filemark : 1; // bytes 3-6 unsigned char information[4]; // byte 7 unsigned char additionalSenseLength; // bytes 8 - 11 unsigned char commandSpecificInformation[4]; // Additional sense code (byte 12)) unsigned char ASC; // Additional sense code qualifier (byte 13) unsigned char ASCQ; // bytes 14 unsigned char fieldReplaceableUnitCode; // bytes 15-17 unsigned char senseSpecificInformation[3]; // bytes 18 onwards unsigned char aditionalSenseBuffer[n - 18]; } fixedFormat; // Helper functions for common fields // First make the difference between the fixed/descriptor // and current/deffered }; bool isFixedFormat() { return responseCode == 0x70 || responseCode == 0x71; } bool isDescriptorFormat() { return responseCode == 0x72 || responseCode == 0x73; } bool isCurrent() { return responseCode == 0x70 || responseCode == 0x72; } bool isDeffered() { return responseCode == 0x71 || responseCode == 0x73; } uint8_t getASC() { if (isFixedFormat()) { return fixedFormat.ASC; } else if (isDescriptorFormat()) { return descriptorFormat.ASC; } else { throw Tape::Exception("In senseData_t::getASC: no ACS with this response code or response code not supported"); } } uint8_t getASCQ() { if (isFixedFormat()) { return fixedFormat.ASCQ; } else if (isDescriptorFormat()) { return descriptorFormat.ASCQ; } else { throw Tape::Exception("In senseData_t::getASCQ: no ACSQ with this response code or response code not supported"); } }; }; template std::string toString(const char(& t)[n]) { std::stringstream r; r.write(t, std::find(t, t + n, '\0') - t); return r.str(); } std::string toString(const inquiryData_t &); template std::string hexDump(const unsigned char(& d)[n]) { std::stringstream hex; hex << std::hex << std::setfill('0'); int pos = 0; while (pos < (8* (n / 8))) { hex << std::setw(4) << pos << " | "; for (int i=0; i<8; i++) hex << std::setw(2) << ((int) d[pos + i]) << " "; hex << "| "; for (int i=0; i<8; i++) hex << std::setw(0) << d[pos + i]; hex << std::endl; pos += 8; } if (n % 8) { hex << std::setw(4) << pos << " | "; for (int i=0; i<(n % 8); i++) hex << std::setw(2) << ((int) d[pos + i]) << " "; for (int i=(n % 8); i<8; i++) hex << " "; hex << "| "; for (int i=0; i<(n % 8); i++) hex << std::setw(0) << d[pos + i]; hex << std::endl; } return hex.str(); } }; };