Commit 81dde87b authored by Eric Cano's avatar Eric Cano
Browse files

Changed all the char arrays used as storage to SCSI number into unsigned...

Changed all the char arrays used as storage to SCSI number into unsigned char[] (we were in a mixed situation).
Added unit test for the recently created structures.
Replaced shift and or with toU16 in log select unit test.
Added reference to st man page and txt file in documentation.
Changed signature of getTapeError.
parent 833d1fa7
......@@ -86,6 +86,12 @@ unless we want to control command queueing with separate sending of commands and
A collection of links to various documentations written in the past is available on one of CAStor's web pages
\footnote{ \href{http://castorwww.web.cern.ch/castorwww/links.htm}{http://castorwww.web.cern.ch/castorwww/links.htm} }.
\subsection{SCSI tape support in Linux (st driver)}
Generic SCSI allows detailed control of the operations, but the bulk of them (including reading and
writing) can be managed by the higher level SCSI tape (or st) driver provided by the Linux kernel.
More information on the st driver can be found in the man page "st" and in \verb#Documentation/scsi/st.txt#
in the sources of the kernel.
\section{Tools used during development}
\subsection{Required tools for build}
\begin{itemize}
......
......@@ -68,6 +68,7 @@ namespace Tape {
};
/**
*
*/
class driveStatus {
bool ready;
......@@ -76,9 +77,19 @@ namespace Tape {
bool eod;
bool bot;
};
class tapeError {
std::string error;
/* TODO: error code. See gettperror and get_sk_msg in CAStor */
};
/**
* Class abstracting the tape drives. Gets initialized from
* Class abstracting the tape drives. This class is templated to allow the use
* of unrelated test harness and real system. The test harness is made up of
* a classes with virtual tables, but the real system wrapper has the real
* system call directly into inline functions. This allows testing on a "fake"
* system without paying performance price when calling system calls in the
* production system.
*/
template <class sysWrapperClass>
class Drive {
......@@ -149,8 +160,8 @@ namespace Tape {
if (SCSI::Status::GOOD != sgh.status)
throw Tape::Exception(std::string("SCSI error in getTapeAlerts: ") +
SCSI::statusToString(sgh.status));
/* Return the ACTIVE tape alerts (this is indicated but the flag (see
* SSC-4: 8.2.3 TapeAlert log page). As they are simply used for logging,
/* Return the ACTIVE tape alerts (this is indicated by "flag" (see
* SSC-4: 8.2.3 TapeAlert log page). As they are simply used for logging;
* return strings. */
for (int i=0; i< tal.parameterNumber(); i++) {
if (tal.parameters[i].flag)
......@@ -173,10 +184,35 @@ namespace Tape {
virtual driveStatus getDriveStatus() throw (Exception) { throw Exception("Not implemented"); }
/**
*
* @return string containing the error description
* getTapeError: get SENSE buffer from patched version of the driver
* or fall back to other information and report tape statuses.
* Statuses were in CAStor struct sk_info sk_codmsg[] = {
* {"No sense", ETNOSNS},
* {"Recovered error", 0},
* {"Not ready", 0},
* {"Medium error", ETPARIT},
* {"Hardware error", ETHWERR},
* {"Illegal request", ETHWERR},
* {"Unit attention", ETHWERR},
* {"Data protect", 0},
* {"Blank check", ETBLANK},
* {"Vendor unique", 0},
* {"Copy aborted", 0},
* {"Aborted command", 0},
* {"Equal", 0},
* {"Volume overflow", ENOSPC},
* {"Miscompare", 0},
* {"Reserved", 0},
* {"SCSI handshake failure", ETHWERR},
* {"Timeout", ETHWERR},
* {"EOF hit", 0},
* {"EOT hit", ETBLANK},
* {"Length error", ETCOMPA},
* {"BOT hit", ETUNREC},
* {"Wrong tape media", ETCOMPA}
* @return error code and string containing the error description
*/
virtual std::string getTapeError() throw (Exception) { throw Exception("Not implemented"); }
virtual tapeError getTapeError() throw (Exception) { throw Exception("Not implemented"); }
virtual ~Drive() {
if(-1 != m_tapeFD)
......
......@@ -89,7 +89,7 @@ namespace SCSI {
* @param t byte array in SCSI order representing a 32 bits number
* @return
*/
inline uint32_t toU32(const char(& t)[4])
inline uint32_t toU32(const unsigned char(& t)[4])
{
/* Like network, SCSI is BigEndian */
return ntohl (*((uint32_t *) t));
......@@ -100,7 +100,7 @@ namespace SCSI {
* @param t byte array in SCSI order representing a 16 bits number
* @return
*/
inline uint16_t toU16(const char(& t)[2])
inline uint16_t toU16(const unsigned char(& t)[2])
{
/* Like network, SCSI is BigEndian */
return ntohs (*((uint16_t *) t));
......@@ -176,7 +176,7 @@ namespace SCSI {
unsigned char reserved1;
char versionDescriptor[8][2];
unsigned char versionDescriptor[8][2];
unsigned char reserved2[22];
unsigned char vendorSpecific2[1];
......@@ -231,7 +231,7 @@ namespace SCSI {
unsigned char pageCode : 6;
unsigned char PC : 2;
unsigned char subPage;
unsigned char subPageCode;
unsigned char reserved;
......@@ -246,7 +246,7 @@ namespace SCSI {
class tapeAlertLogParameter_t {
public:
char parameterCode [2];
unsigned char parameterCode [2];
unsigned char formatAndLinking : 2;
unsigned char TMC : 2;
......@@ -262,7 +262,7 @@ namespace SCSI {
};
/**
* Tape alert log mage, returned by LOG SENSE. Defined in SSC-3, section 8.2.3 TapeAler log page.
* Tape alert log page, returned by LOG SENSE. Defined in SSC-3, section 8.2.3 TapeAler log page.
*/
template <int n>
class tapeAlertLogPage_t {
......@@ -272,18 +272,17 @@ namespace SCSI {
unsigned char subPageCode;
char pageLength[2];
unsigned char pageLength[2];
tapeAlertLogParameter_t parameters [n];
/**
* Utility function computing the number of parameters.
* 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 (tapeAlertLogPage_t);
if (numFromLength > n)
throw Tape::Exception("In tapeAlertLogPage_t::parameterNumber: too many parameters from device");
int numFromLength = SCSI::Structures::toU16(pageLength) / sizeof (tapeAlertLogParameter_t);
return numFromLength;
}
tapeAlertLogPage_t() { zeroStruct(this); }
......
......@@ -151,8 +151,113 @@ namespace UnitTests {
ASSERT_EQ(0xBC, logSelectCDB.subPageCode);
/* ... */
buff[7] |= 0xAB; buff[8] |= 0xCD;
ASSERT_EQ(0xABCD, logSelectCDB.parameterListLength[0]<<8|logSelectCDB.parameterListLength[1]);
ASSERT_EQ(0xABCD, SCSI::Structures::toU16(logSelectCDB.parameterListLength));
buff[9] |= 0xBC;
ASSERT_EQ(0xBC, logSelectCDB.control);
}
TEST(SCSI_Structures, LinuxSGIO_t) {
ASSERT_EQ(sizeof(sg_io_hdr_t), sizeof(SCSI::Structures::LinuxSGIO_t));
SCSI::Structures::LinuxSGIO_t lsg;
/* Most important part: check that the class does not add data
to the original structure (virtual table, for example)*/
sg_io_hdr_t & sgio_hdr = *(sg_io_hdr_t *)&lsg;
/* Also make sure the constructor does its initialization job */
ASSERT_EQ(30000, sgio_hdr.timeout);
ASSERT_EQ('S', sgio_hdr.interface_id);
/* The rest is safe. It's just a struct with added functions */
}
TEST(SCSI_Structures, logSenseCDB_t) {
SCSI::Structures::logSenseCDB_t logSenseCDB;
unsigned char *buff = (unsigned char *)&logSenseCDB;
/*
* Make sure this struct is a POD (plain old data without virtual table)
* (and has the right size).
*/
ASSERT_EQ(10, sizeof(logSenseCDB));
/* Check proper initialization an location of struct members match
the bit/byte locations defined in SPC-4 */
ASSERT_EQ(SCSI::Commands::LOG_SENSE, logSenseCDB.opCode);
buff[0] = 0xAB;
ASSERT_EQ(0xAB, logSenseCDB.opCode);
ASSERT_EQ(0, logSenseCDB.SP);
buff[1] |= (0x1 & 0x7) << 0;
ASSERT_EQ(1, logSenseCDB.SP);
ASSERT_EQ(0, logSenseCDB.PPC);
buff[1] |= (0x1 & 0xF) << 1;
ASSERT_EQ(1, logSenseCDB.PPC);
ASSERT_EQ(0, logSenseCDB.pageCode);
buff[2] |= (0xAB & 0x3F) << 0;
ASSERT_EQ(0x2B, logSenseCDB.pageCode);
ASSERT_EQ(0, logSenseCDB.PC);
buff[2] |= (0x2 & 0x3) << 6;
ASSERT_EQ(0x2, logSenseCDB.PC);
ASSERT_EQ(0, logSenseCDB.subPageCode);
buff[3] = 0xBC;
ASSERT_EQ(0xBC, logSenseCDB.subPageCode);
ASSERT_EQ(0, SCSI::Structures::toU16(logSenseCDB.parameterPointer));
buff[5] = 0x12; buff[6] = 0x34;
ASSERT_EQ(0x1234, SCSI::Structures::toU16(logSenseCDB.parameterPointer));
ASSERT_EQ(0, SCSI::Structures::toU16(logSenseCDB.allocationLength));
buff[7] |= 0xAB; buff[8] |= 0xCD;
ASSERT_EQ(0xABCD, SCSI::Structures::toU16(logSenseCDB.allocationLength));
ASSERT_EQ(0, logSenseCDB.control);
buff[9] |= 0xBC;
ASSERT_EQ(0xBC, logSenseCDB.control);
}
TEST(SCSI_Structures, tapeAlertLogPage_t_and_parameters) {
SCSI::Structures::tapeAlertLogPage_t<12> tal;
unsigned char * buff = (unsigned char *) & tal;
/* Check the size of the structure (header is 4 bytes, array elements, 5.*/
/* As usual, this ensures we have POD */
ASSERT_EQ(4 + 12*5, sizeof(tal));
ASSERT_EQ(0, tal.pageCode);
buff[0] |= (0x12 & 0x3F) << 0;
ASSERT_EQ(0x12, tal.pageCode);
ASSERT_EQ(0,tal.subPageCode);
buff[1] = 0x34;
ASSERT_EQ(0x34, tal.subPageCode);
/* Simulate 123 records = 600 bytes = 0x267 */
ASSERT_EQ(0, SCSI::Structures::toU16(tal.pageLength));
buff[2] = 0x2; buff[3]= 0x67;
ASSERT_EQ(0x267, SCSI::Structures::toU16(tal.pageLength));
/* The page length is counted in bytes. We are interested in the number of parameters*/
ASSERT_EQ(123, tal.parameterNumber());
ASSERT_EQ(0, SCSI::Structures::toU16(tal.parameters[0].parameterCode));
buff [4] = 0xCA; buff[5] = 0xFE;
ASSERT_EQ(0xCAFE, SCSI::Structures::toU16(tal.parameters[0].parameterCode));
ASSERT_EQ(0, tal.parameters[11].flag);
buff[3+12*5] |= (0x1) << 0;
ASSERT_EQ(1, tal.parameters[11].flag);
}
TEST(SCSI_Structures, toto) {}
TEST(SCSI_Structures, toU16) {
unsigned char num[2] = { 0x1, 0x2 };
ASSERT_EQ( 0x102, SCSI::Structures::toU16(num));
}
TEST(SCSI_Structures, toU32) {
unsigned char num[4] = { 0x1, 0x2, 0x3, 0x4 };
ASSERT_EQ( 0x1020304, SCSI::Structures::toU32(num));
}
};
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment