package org.jscsi.target.scsi.cdb;
import java.nio.ByteBuffer;
import org.jscsi.parser.scsi.SCSICommandParser;
import org.jscsi.target.scsi.sense.AdditionalSenseCodeAndQualifier;
import org.jscsi.target.scsi.sense.SenseData;
import org.jscsi.target.scsi.sense.SenseKey;
import org.jscsi.target.scsi.sense.senseDataDescriptor.senseKeySpecific.FieldPointerSenseKeySpecificData;
import org.jscsi.target.util.BitManip;
/**
* An abstract class for accessing the variables common to Command Descriptor
* Blocks of all sizes.
* <p>
* A Command Descriptor Blocks is a blocks of multiple bytes that contains a {@link ScsiOperationCode},
* specifying a command the SCSI initiator wants the SCSI target to perform and related fields, which
* determine details of the ordered task.
* <p>
* Since all fields in the Control byte except for the Normal ACA bit are either reserved, obsolete, or vendor
* specific, the NACA bit can be accessed directly from this class.
*
* @author Andreas Ergenzinger
*/
public abstract class CommandDescriptorBlock {
/**
* Not a CDB field. This array stores all {@link FieldPointerSenseKeySpecificData} that has to be sent
* back to the
* SCSI initiator as a reaction to the CDB {@link ByteBuffer} passed during
* initialization of this {@link CommandDescriptorBlock} instance.
* <p>
* If, during the parsing process, an illegal value is detected, the array will be initialized and a
* {@link FieldPointerSenseKeySpecificData} object, specifying the position of the illegal field, will be
* added.
*/
private FieldPointerSenseKeySpecificData[] illegalFieldPointers = null;
/**
* Determines which command to perform.
*/
private ScsiOperationCode scsiOperationCode;
/**
* Specifies if Normal ACA shall be used. A value of <code>true</code> is
* not supported by the jSCSI Target.
*/
private boolean normalAutoContingentAllegiance;
/**
* The abstract constructor.
* <p>
* Deserializes the first byte containing the SCSI Operation Code and the Control byte.
* <p>
* The passed {@link ByteBuffer} parameter <b>must</b> have a capacity ≥ the length of the specific
* command descriptor block. Since this is assured by the {@link SCSICommandParser#getCDB()} method, which
* always returns {@link ByteBuffer} objects with capacity 16, no checks will be performed.
*
* @param buffer
* contains the serialized CDB starting at index position zero
*/
public CommandDescriptorBlock(ByteBuffer buffer) {
// SCSI OpCode
scsiOperationCode = ScsiOperationCode.valueOf(buffer.get(0));
if (scsiOperationCode == null)// unsupported OpCode
addIllegalFieldPointer(0);
/*
* The above if block should never be entered, since unsupported
* operation codes in the CDB would have been discovered during
* TargetFullFeatureStage selection in TargetFullFeaturePhase.execute().
*/
// Control (NACA bit, all other fields are obsolete, reserved, or vendor
// specific)
final CdbType cdbType = scsiOperationCode.getCdbType();
int controlByteIndex;
switch (cdbType) {
case SIX_BYTE_COMMANDS:
controlByteIndex = 5;
break;
case TEN_BYTE_COMMANDS:
controlByteIndex = 9;
break;
case TWELVE_BYTE_COMMANDS:
controlByteIndex = 11;
break;
case SIXTEEN_BYTE_COMMANDS:
controlByteIndex = 15;
break;
default:
controlByteIndex = -1;
/*
* Would lead to ArrayOutOfBoundsException, however, this will not
* happen, since unsupported group codes will be filtered in
* TargetFullFeaturePhase.execute() (see above).
*/
}
normalAutoContingentAllegiance = BitManip.getBit(buffer.get(controlByteIndex), 2);
if (normalAutoContingentAllegiance) {// normalACA is not supported
addIllegalFieldPointer(controlByteIndex, 2);
}
}
public final ScsiOperationCode getScsiOperationCode() {
return scsiOperationCode;
}
/**
* The NACA (Normal ACA) bit specifies whether an auto contingent allegiance
* (ACA) is established if the command terminates with CHECK CONDITION
* status. A NACA bit set to one specifies that an ACA shall be established.
* A NACA bit set to zero specifies that an ACA shall not be established.
*
* @return <code>true</code> if the Normal ACA bit in the CDB's Control byte
* is set and <code>false</code> if it is not.
*/
public final boolean isNormalACA() {
return normalAutoContingentAllegiance;
}
/**
* Adds an instance {@link FieldPointerSenseKeySpecificData}, which
* specifies an illegal field by the position of its first byte, to {@link #illegalFieldPointers}.
*
* @param byteNumber
* index of the first byte of the illegal field
*/
protected final void addIllegalFieldPointer(int byteNumber) {
final FieldPointerSenseKeySpecificData fp = new FieldPointerSenseKeySpecificData(true,// senseKeySpecificDataValid
true,// commandData (i.e. invalid field in CDB)
false,// bitPointerValid
0,// bitPointer
byteNumber);// fieldPointer
addIllegalFieldPointer(fp);
}
/**
* Adds an instance {@link FieldPointerSenseKeySpecificData}, which
* specifies an illegal field by the position of its first byte and its
* first bit, to {@link #illegalFieldPointers}.
*
* @param byteNumber
* index of the first byte of the illegal field
* @param bitNumber
* index of the first bit of the illegal field
*/
protected final void addIllegalFieldPointer(int byteNumber, int bitNumber) {
FieldPointerSenseKeySpecificData fp = new FieldPointerSenseKeySpecificData(true,// senseKeySpecificDataValid
true,// commandData (i.e. invalid field in CDB)
true,// bitPointerValid
bitNumber,// bitPointer
byteNumber);// fieldPointer
addIllegalFieldPointer(fp);
}
/**
* Adds a {@link FieldPointerSenseKeySpecificData} object to {@link #illegalFieldPointers}. Initializes
* and grows the array if
* necessary.
*
* @param illegalFieldPointer
* the object to add
*/
private final void addIllegalFieldPointer(final FieldPointerSenseKeySpecificData illegalFieldPointer) {
// grow array?
if (illegalFieldPointers == null)
illegalFieldPointers = new FieldPointerSenseKeySpecificData[10];
final int size = getIllegalFieldPointerSize();
if (size >= illegalFieldPointers.length) {
// grow
FieldPointerSenseKeySpecificData[] temp =
new FieldPointerSenseKeySpecificData[illegalFieldPointers.length + 1];
for (int i = 0; i < size; ++i) {
temp[i] = illegalFieldPointers[i];
}
illegalFieldPointers = temp;
}
// add new element
illegalFieldPointers[size] = illegalFieldPointer;
}
/**
* Returns the number of elements stored in {@link #illegalFieldPointers}.
*
* @return the number of elements stored in {@link #illegalFieldPointers}
*/
private final int getIllegalFieldPointerSize() {
if (illegalFieldPointers == null)
return 0;
int size = 0;
while (size < illegalFieldPointers.length) {
if (illegalFieldPointers[size] != null)
++size;
else
break;
}
return size;
}
/**
* Returns <code>null</code> if there were no illegal fields in the
* serialized CDB passed to the constructor, or an array of appropriate
* {@link FieldPointerSenseKeySpecificData}, that have to be enclosed in the {@link SenseData} returned to
* the initiator (with sense key {@link SenseKey#ILLEGAL_REQUEST} and additional sense code and sense code
* qualifier {@link AdditionalSenseCodeAndQualifier#INVALID_FIELD_IN_CDB}).
*
* @return <code>null</code> or an array of appropriate {@link FieldPointerSenseKeySpecificData}
*/
public final FieldPointerSenseKeySpecificData[] getIllegalFieldPointers() {
if (illegalFieldPointers == null)
return null;
// returned trimmed array without null values
final int size = getIllegalFieldPointerSize();
FieldPointerSenseKeySpecificData[] result = new FieldPointerSenseKeySpecificData[size];
for (int i = 0; i < size; ++i) {
result[i] = illegalFieldPointers[i];
}
return result;
}
}