From 3b318a56e31204102708c9a9987c86d76b98d488 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 13 Jan 2016 03:47:25 +0000 Subject: [PATCH] * DiscImageChef.Devices/Device/ScsiCommands/SMC.cs: * DiscImageChef.Devices/DiscImageChef.Devices.csproj: Added read-only Streaming Commands. * DiscImageChef.Devices/Device/ScsiCommands/SSC.cs: Added READ ATTRIBUTE. * DiscImageChef.Devices/Device/ScsiCommands/MMC.cs: Corrected typo on header * DiscImageChef.Devices/Device/ScsiCommands/SPC.cs: Corrected buffer mislength. Added support for READ ATTRIBUTE. * DiscImageChef.Devices/Enums.cs: Corrected opcode for SCSI WRITE ATTRIBUTE. Added SCSI Streaming and Attribute enumerations. --- DiscImageChef.Devices/ChangeLog | 20 + .../Device/ScsiCommands/MMC.cs | 2 +- .../Device/ScsiCommands/SMC.cs | 105 ++ .../Device/ScsiCommands/SPC.cs | 97 +- .../Device/ScsiCommands/SSC.cs | 986 ++++++++++++++++++ .../DiscImageChef.Devices.csproj | 2 + DiscImageChef.Devices/Enums.cs | 90 +- 7 files changed, 1298 insertions(+), 4 deletions(-) create mode 100644 DiscImageChef.Devices/Device/ScsiCommands/SMC.cs create mode 100644 DiscImageChef.Devices/Device/ScsiCommands/SSC.cs diff --git a/DiscImageChef.Devices/ChangeLog b/DiscImageChef.Devices/ChangeLog index 8f882b09..5cbb4e3e 100644 --- a/DiscImageChef.Devices/ChangeLog +++ b/DiscImageChef.Devices/ChangeLog @@ -1,3 +1,23 @@ +2016-01-13 Natalia Portillo + + * Device/ScsiCommands/SMC.cs: + * DiscImageChef.Devices.csproj: + Added read-only Streaming Commands. + + * Device/ScsiCommands/SSC.cs: + Added READ ATTRIBUTE. + + * Device/ScsiCommands/MMC.cs: + Corrected typo on header + + * Device/ScsiCommands/SPC.cs: + Corrected buffer mislength. + Added support for READ ATTRIBUTE. + + * Enums.cs: + Corrected opcode for SCSI WRITE ATTRIBUTE. + Added SCSI Streaming and Attribute enumerations. + 2016-01-11 Natalia Portillo * Device/ScsiCommands.cs: diff --git a/DiscImageChef.Devices/Device/ScsiCommands/MMC.cs b/DiscImageChef.Devices/Device/ScsiCommands/MMC.cs index d6b77020..c65704c3 100644 --- a/DiscImageChef.Devices/Device/ScsiCommands/MMC.cs +++ b/DiscImageChef.Devices/Device/ScsiCommands/MMC.cs @@ -2,7 +2,7 @@ // The Disc Image Chef // ---------------------------------------------------------------------------- // -// Filename : MmcCommands.cs +// Filename : MMC.cs // Version : 1.0 // Author(s) : Natalia Portillo // diff --git a/DiscImageChef.Devices/Device/ScsiCommands/SMC.cs b/DiscImageChef.Devices/Device/ScsiCommands/SMC.cs new file mode 100644 index 00000000..e5266279 --- /dev/null +++ b/DiscImageChef.Devices/Device/ScsiCommands/SMC.cs @@ -0,0 +1,105 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : SMC.cs +// Version : 1.0 +// Author(s) : Natalia Portillo +// +// Component : SCSI Media-changer Commands +// +// Revision : $Revision$ +// Last change by : $Author$ +// Date : $Date$ +// +// --[ Description ] ---------------------------------------------------------- +// +// Description +// +// --[ License ] -------------------------------------------------------------- +// +// 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 . +// +// ---------------------------------------------------------------------------- +// Copyright (C) 2011-2015 Claunia.com +// ****************************************************************************/ +// //$Id$ +using System; +using DiscImageChef.Console; + +namespace DiscImageChef.Devices +{ + public partial class Device + { + /// + /// Reads an attribute from the medium auxiliary memory, or reports which elements in the changer contain one + /// + /// Buffer. + /// Sense buffer. + /// What to do, . + /// Element address. + /// Element type. + /// Volume number. + /// Partition number. + /// First attribute identificator. + /// If set to true device can return cached data. + /// Timeout. + /// Duration. + public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, ushort element, byte elementType, byte volume, byte partition, ushort firstAttribute, bool cache, uint timeout, out double duration) + { + buffer = new byte[256]; + byte[] cdb = new byte[16]; + senseBuffer = new byte[32]; + bool sense; + + cdb[0] = (byte)ScsiCommands.ReadAttribute; + cdb[1] = (byte)((byte)action & 0x1F); + cdb[2] = (byte)((element & 0xFF00) >> 8); + cdb[3] = (byte)(element & 0xFF); + cdb[4] = (byte)(elementType & 0x0F); + cdb[5] = volume; + cdb[7] = partition; + cdb[8] = (byte)((firstAttribute & 0xFF00) >> 8); + cdb[9] = (byte)(firstAttribute & 0xFF); + cdb[10] = (byte)((buffer.Length & 0xFF000000) >> 24); + cdb[11] = (byte)((buffer.Length & 0xFF0000) >> 16); + cdb[12] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[13] = (byte)(buffer.Length & 0xFF); + if (cache) + cdb[14] += 0x01; + + lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, out sense); + error = lastError != 0; + + if (sense) + return true; + + uint attrLen = (uint)(((int)buffer[0] << 24) + ((int)buffer[1] << 16) + ((int)buffer[2] << 8) + buffer[3] + 4); + buffer = new byte[attrLen]; + cdb[10] = (byte)((buffer.Length & 0xFF000000) >> 24); + cdb[11] = (byte)((buffer.Length & 0xFF0000) >> 16); + cdb[12] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[13] = (byte)(buffer.Length & 0xFF); + senseBuffer = new byte[32]; + + lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, out sense); + error = lastError != 0; + + DicConsole.DebugWriteLine("SCSI Device", "READ ATTRIBUTE took {0} ms.", duration); + + return sense; + } + } +} + diff --git a/DiscImageChef.Devices/Device/ScsiCommands/SPC.cs b/DiscImageChef.Devices/Device/ScsiCommands/SPC.cs index 86bf186e..f4375c0a 100644 --- a/DiscImageChef.Devices/Device/ScsiCommands/SPC.cs +++ b/DiscImageChef.Devices/Device/ScsiCommands/SPC.cs @@ -2,7 +2,7 @@ // The Disc Image Chef // ---------------------------------------------------------------------------- // -// Filename : SpcCommands.cs +// Filename : SPC.cs // Version : 1.0 // Author(s) : Natalia Portillo // @@ -583,11 +583,11 @@ namespace DiscImageChef.Devices return true; uint strctLength = (uint)(((int)buffer[0] << 24) + ((int)buffer[1] << 16) + ((int)buffer[2] << 8) + buffer[3] + 4); + buffer = new byte[strctLength]; cdb[6] = (byte)((buffer.Length & 0xFF000000) >> 24); cdb[7] = (byte)((buffer.Length & 0xFF0000) >> 16); cdb[8] = (byte)((buffer.Length & 0xFF00) >> 8); cdb[9] = (byte)(buffer.Length & 0xFF); - buffer = new byte[strctLength]; senseBuffer = new byte[32]; lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, out sense); @@ -597,6 +597,99 @@ namespace DiscImageChef.Devices return sense; } + + /// + /// Reads an attribute from the medium auxiliary memory + /// + /// Buffer. + /// Sense buffer. + /// What to do, . + /// Partition number. + /// First attribute identifier. + /// If set to true device can return cached data. + /// Timeout. + /// Duration. + public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, byte partition, ushort firstAttribute, bool cache, uint timeout, out double duration) + { + return ReadAttribute(out buffer, out senseBuffer, action, 0, 0, 0, partition, firstAttribute, cache, timeout, out duration); + } + + /// + /// Reads an attribute from the medium auxiliary memory + /// + /// Buffer. + /// Sense buffer. + /// What to do, . + /// First attribute identifier. + /// If set to true device can return cached data. + /// Timeout. + /// Duration. + public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, ushort firstAttribute, bool cache, uint timeout, out double duration) + { + return ReadAttribute(out buffer, out senseBuffer, action, 0, 0, 0, 0, firstAttribute, cache, timeout, out duration); + } + + /// + /// Reads an attribute from the medium auxiliary memory + /// + /// Buffer. + /// Sense buffer. + /// What to do, . + /// Partition number. + /// First attribute identifier. + /// Timeout. + /// Duration. + public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, byte partition, ushort firstAttribute, uint timeout, out double duration) + { + return ReadAttribute(out buffer, out senseBuffer, action, 0, 0, 0, partition, firstAttribute, false, timeout, out duration); + } + + /// + /// Reads an attribute from the medium auxiliary memory + /// + /// Buffer. + /// Sense buffer. + /// What to do, . + /// First attribute identifier. + /// Timeout. + /// Duration. + public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, ushort firstAttribute, uint timeout, out double duration) + { + return ReadAttribute(out buffer, out senseBuffer, action, 0, 0, 0, 0, firstAttribute, false, timeout, out duration); + } + + /// + /// Reads an attribute from the medium auxiliary memory + /// + /// Buffer. + /// Sense buffer. + /// What to do, . + /// Volume number. + /// Partition number. + /// First attribute identifier. + /// Timeout. + /// Duration. + public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, byte volume, byte partition, ushort firstAttribute, uint timeout, out double duration) + { + return ReadAttribute(out buffer, out senseBuffer, action, 0, 0, volume, partition, firstAttribute, false, timeout, out duration); + } + + /// + /// Reads an attribute from the medium auxiliary memory + /// + /// Buffer. + /// Sense buffer. + /// What to do, . + /// Volume number. + /// Partition number. + /// First attribute identifier. + /// If set to true device can return cached data. + /// Timeout. + /// Duration. + public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, byte volume, byte partition, ushort firstAttribute, bool cache, uint timeout, out double duration) + { + return ReadAttribute(out buffer, out senseBuffer, action, 0, 0, volume, partition, firstAttribute, cache, timeout, out duration); + } } } diff --git a/DiscImageChef.Devices/Device/ScsiCommands/SSC.cs b/DiscImageChef.Devices/Device/ScsiCommands/SSC.cs new file mode 100644 index 00000000..680e5076 --- /dev/null +++ b/DiscImageChef.Devices/Device/ScsiCommands/SSC.cs @@ -0,0 +1,986 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : SSC.cs +// Version : 1.0 +// Author(s) : Natalia Portillo +// +// Component : SCSI Stream Commands +// +// Revision : $Revision$ +// Last change by : $Author$ +// Date : $Date$ +// +// --[ Description ] ---------------------------------------------------------- +// +// Description +// +// --[ License ] -------------------------------------------------------------- +// +// 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 . +// +// ---------------------------------------------------------------------------- +// Copyright (C) 2011-2015 Claunia.com +// ****************************************************************************/ +// //$Id$ +using System; +using DiscImageChef.Console; + +namespace DiscImageChef.Devices +{ + public partial class Device + { + /// + /// Prepares the medium for reading + /// + /// true, if load was successful, false otherwise. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool Load(out byte[] senseBuffer, uint timeout, out double duration) + { + return LoadUnload(out senseBuffer, false, true, false, false, false, timeout, out duration); + } + + /// + /// Prepares the medium for ejection + /// + /// true, if unload was successful, false otherwise. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool Unload(out byte[] senseBuffer, uint timeout, out double duration) + { + return LoadUnload(out senseBuffer, false, false, false, false, false, timeout, out duration); + } + + /// + /// Prepares the medium for reading or ejection + /// + /// true, if load/unload was successful, false otherwise. + /// Sense buffer. + /// If set to true, return from the command immediately. + /// If set to true load the medium for reading. + /// If set to true retense the tape. + /// If set to true move the medium to the EOT mark. + /// If set to true and is also set to true, moves the medium to the drive but does not prepare it for reading. + /// Timeout. + /// Duration. + public bool LoadUnload(out byte[] senseBuffer, bool immediate, bool load, bool retense, bool endOfTape, bool hold, uint timeout, out double duration) + { + senseBuffer = new byte[32]; + byte[] cdb = new byte[6]; + byte[] buffer = new byte[0]; + bool sense; + + cdb[0] = (byte)ScsiCommands.LoadUnload; + if (immediate) + cdb[1] = 0x01; + if (load) + cdb[4] += 0x01; + if (retense) + cdb[4] += 0x02; + if (endOfTape) + cdb[4] += 0x04; + if (hold) + cdb[4] += 0x08; + + lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, out sense); + error = lastError != 0; + + DicConsole.DebugWriteLine("SCSI Device", "LOAD UNLOAD (6) took {0} ms.", duration); + + return sense; + } + + /// + /// Positions the medium to the specified block in the current partition + /// + /// Sense buffer. + /// Logical block address. + /// Timeout. + /// Duration. + public bool Locate(out byte[] senseBuffer, uint lba, uint timeout, out double duration) + { + return Locate(out senseBuffer, false, false, true, 0, lba, timeout, out duration); + } + + /// + /// Positions the medium to the specified block in the specified partition + /// + /// Sense buffer. + /// Partition to position to. + /// Logical block address. + /// Timeout. + /// Duration. + public bool Locate(out byte[] senseBuffer, byte partition, uint lba, uint timeout, out double duration) + { + return Locate(out senseBuffer, false, false, true, partition, lba, timeout, out duration); + } + + /// + /// Positions the medium to the specified block in the current partition + /// + /// Sense buffer. + /// If set to true, return from the command immediately. + /// Logical block address. + /// Timeout. + /// Duration. + public bool Locate(out byte[] senseBuffer, bool immediate, uint lba, uint timeout, out double duration) + { + return Locate(out senseBuffer, immediate, false, true, 0, lba, timeout, out duration); + } + + /// + /// Positions the medium to the specified block in the specified partition + /// + /// Sense buffer. + /// If set to true, return from the command immediately. + /// Partition to position to. + /// Logical block address. + /// Timeout. + /// Duration. + public bool Locate(out byte[] senseBuffer, bool immediate, byte partition, uint lba, uint timeout, out double duration) + { + return Locate(out senseBuffer, immediate, false, true, partition, lba, timeout, out duration); + } + + /// + /// Positions the medium to the specified object identifier + /// + /// Sense buffer. + /// If set to true, return from the command immediately. + /// If set to true object identifier is vendor specified. + /// If set to true change partition. + /// Partition to position to. + /// Object identifier. + /// Timeout. + /// Duration. + public bool Locate(out byte[] senseBuffer, bool immediate, bool blockType, bool changePartition, byte partition, uint objectId, uint timeout, out double duration) + { + senseBuffer = new byte[32]; + byte[] cdb = new byte[10]; + byte[] buffer = new byte[0]; + bool sense; + + cdb[0] = (byte)ScsiCommands.Locate; + if (immediate) + cdb[1] += 0x01; + if (changePartition) + cdb[1] += 0x02; + if (blockType) + cdb[1] += 0x04; + cdb[3] = (byte)((objectId & 0xFF000000) >> 24); + cdb[4] = (byte)((objectId & 0xFF0000) >> 16); + cdb[5] = (byte)((objectId & 0xFF00) >> 8); + cdb[6] = (byte)(objectId & 0xFF); + cdb[8] = partition; + + lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, out sense); + error = lastError != 0; + + DicConsole.DebugWriteLine("SCSI Device", "LOCATE (10) took {0} ms.", duration); + + return sense; + } + + /// + /// Positions the medium to the specified block in the current partition + /// + /// Sense buffer. + /// Logical block address. + /// Timeout. + /// Duration. + public bool Locate16(out byte[] senseBuffer, ulong lba, uint timeout, out double duration) + { + return Locate16(out senseBuffer, false, false, SscLogicalIdTypes.ObjectId, false, 0, lba, timeout, out duration); + } + + /// + /// Positions the medium to the specified block in the specified partition + /// + /// Sense buffer. + /// Partition to position to. + /// Logical block address. + /// Timeout. + /// Duration. + public bool Locate16(out byte[] senseBuffer, byte partition, ulong lba, uint timeout, out double duration) + { + return Locate16(out senseBuffer, false, true, SscLogicalIdTypes.ObjectId, false, partition, lba, timeout, out duration); + } + + /// + /// Positions the medium to the specified block in the current partition + /// + /// Sense buffer. + /// If set to true, return from the command immediately. + /// Logical block address. + /// Timeout. + /// Duration. + public bool Locate16(out byte[] senseBuffer, bool immediate, ulong lba, uint timeout, out double duration) + { + return Locate16(out senseBuffer, immediate, false, SscLogicalIdTypes.ObjectId, false, 0, lba, timeout, out duration); + } + + /// + /// Positions the medium to the specified block in the specified partition + /// + /// Sense buffer. + /// If set to true, return from the command immediately. + /// Partition to position to. + /// Logical block address. + /// Timeout. + /// Duration. + public bool Locate16(out byte[] senseBuffer, bool immediate, byte partition, ulong lba, uint timeout, out double duration) + { + return Locate16(out senseBuffer, immediate, true, SscLogicalIdTypes.ObjectId, false, partition, lba, timeout, out duration); + } + + /// + /// Positions the medium to the specified object identifier + /// + /// Sense buffer. + /// If set to true, return from the command immediately. + /// If set to true change partition. + /// Destination type. + /// If set to true objectId is explicit. + /// Partition to position to. + /// Destination identifier. + /// Timeout. + /// Duration. + public bool Locate16(out byte[] senseBuffer, bool immediate, bool changePartition, SscLogicalIdTypes destType, bool bam, byte partition, ulong identifier, uint timeout, out double duration) + { + senseBuffer = new byte[32]; + byte[] cdb = new byte[16]; + byte[] buffer = new byte[0]; + bool sense; + byte[] idBytes = BitConverter.GetBytes(identifier); + + cdb[0] = (byte)ScsiCommands.Locate16; + cdb[1] = (byte)((byte)destType << 3); + if (immediate) + cdb[1] += 0x01; + if (changePartition) + cdb[1] += 0x02; + if (bam) + cdb[2] = 0x01; + cdb[3] = partition; + + cdb[4] = idBytes[7]; + cdb[5] = idBytes[6]; + cdb[6] = idBytes[5]; + cdb[7] = idBytes[4]; + cdb[8] = idBytes[3]; + cdb[9] = idBytes[2]; + cdb[10] = idBytes[1]; + cdb[11] = idBytes[0]; + + lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, out sense); + error = lastError != 0; + + DicConsole.DebugWriteLine("SCSI Device", "LOCATE (16) took {0} ms.", duration); + + return sense; + } + + /*/// + /// Reads the specified number of blocks from the medium + /// + /// Buffer. + /// Sense buffer. + /// How many blocks to read. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool Read6(out byte[] buffer, out byte[] senseBuffer, uint blocks, uint blockSize, uint timeout, out double duration) + { + return Read6(out buffer, out senseBuffer, false, true, blocks, blockSize, timeout, out duration); + }*/ + + /// + /// Reads the specified number of bytes or of blocks from the medium + /// + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. + /// How many bytes to read. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool Read6(out byte[] buffer, out byte[] senseBuffer, bool sili, uint transferLen, uint blockSize, uint timeout, out double duration) + { + return Read6(out buffer, out senseBuffer, sili, false, transferLen, blockSize, timeout, out duration); + } + + /// + /// Reads the specified number of bytes or of blocks from the medium + /// + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. Cannot be set while is set also. + /// If set to true indicates how many blocks to read of a fixed size. + /// Transfer length in blocks or bytes depending of status. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool Read6(out byte[] buffer, out byte[] senseBuffer, bool sili, bool fixedLen, uint transferLen, uint blockSize, uint timeout, out double duration) + { + if (fixedLen) + buffer = new byte[blockSize * transferLen]; + else + buffer = new byte[transferLen]; + byte[] cdb = new byte[6]; + senseBuffer = new byte[32]; + bool sense; + + cdb[0] = (byte)ScsiCommands.Read6; + if (fixedLen) + cdb[1] += 0x01; + if (sili) + cdb[1] += 0x02; + cdb[2] = (byte)((transferLen & 0xFF0000) >> 16); + cdb[3] = (byte)((transferLen & 0xFF00) >> 8); + cdb[4] = (byte)(transferLen & 0xFF); + + lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, out sense); + error = lastError != 0; + + DicConsole.DebugWriteLine("SCSI Device", "READ (6) took {0} ms.", duration); + + return sense; + } + + /// + /// Reads a number of fixed-length blocks starting at specified object + /// + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. + /// Object identifier. + /// How many blocks to read. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool Read16(out byte[] buffer, out byte[] senseBuffer, bool sili, ulong objectId, uint blocks, uint blockSize, uint timeout, out double duration) + { + return Read16(out buffer, out senseBuffer, sili, false, 0, objectId, blocks, blockSize, timeout, out duration); + } + + /// + /// Reads a number of fixed-length blocks starting at specified block from the specified partition + /// + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. + /// Partition to read object from. + /// Object identifier. + /// How many blocks to read. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool Read16(out byte[] buffer, out byte[] senseBuffer, bool sili, byte partition, ulong objectId, uint blocks, uint blockSize, uint timeout, out double duration) + { + return Read16(out buffer, out senseBuffer, sili, false, partition, objectId, blocks, blockSize, timeout, out duration); + } + + /// + /// Reads a number of fixed-length blocks starting at specified object + /// + /// Buffer. + /// Sense buffer. + /// Object identifier. + /// How many blocks to read. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool Read16(out byte[] buffer, out byte[] senseBuffer, ulong objectId, uint blocks, uint blockSize, uint timeout, out double duration) + { + return Read16(out buffer, out senseBuffer, false, true, 0, objectId, blocks, blockSize, timeout, out duration); + } + + /// + /// Reads a number of fixed-length blocks starting at specified block from the specified partition + /// + /// Buffer. + /// Sense buffer. + /// Partition to read object from. + /// Object identifier. + /// How many blocks to read. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool Read16(out byte[] buffer, out byte[] senseBuffer, byte partition, ulong objectId, uint blocks, uint blockSize, uint timeout, out double duration) + { + return Read16(out buffer, out senseBuffer, false, true, partition, objectId, blocks, blockSize, timeout, out duration); + } + + /// + /// Reads a number of bytes or objects starting at specified object from the specified partition + /// + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. Cannot be set while is set also. + /// If set to true indicates how many blocks to read of a fixed size. + /// Partition to read object from. + /// Object identifier. + /// Transfer length in blocks or bytes depending of status. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool Read16(out byte[] buffer, out byte[] senseBuffer, bool sili, bool fixedLen, byte partition, ulong objectId, uint transferLen, uint objectSize, uint timeout, out double duration) + { + if (fixedLen) + buffer = new byte[objectSize * transferLen]; + else + buffer = new byte[transferLen]; + byte[] cdb = new byte[6]; + senseBuffer = new byte[32]; + bool sense; + byte[] idBytes = BitConverter.GetBytes(objectId); + + cdb[0] = (byte)ScsiCommands.Read16; + if (fixedLen) + cdb[1] += 0x01; + if (sili) + cdb[1] += 0x02; + cdb[3] = partition; + cdb[4] = idBytes[7]; + cdb[5] = idBytes[6]; + cdb[6] = idBytes[5]; + cdb[7] = idBytes[4]; + cdb[8] = idBytes[3]; + cdb[9] = idBytes[2]; + cdb[10] = idBytes[1]; + cdb[11] = idBytes[0]; + cdb[12] = (byte)((transferLen & 0xFF0000) >> 16); + cdb[13] = (byte)((transferLen & 0xFF00) >> 8); + cdb[14] = (byte)(transferLen & 0xFF); + + lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, out sense); + error = lastError != 0; + + DicConsole.DebugWriteLine("SCSI Device", "READ (16) took {0} ms.", duration); + + return sense; + } + + /// + /// Requests the drive the maximum and minimum block size + /// + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool ReadBlockLimits(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + buffer = new byte[6]; + byte[] cdb = new byte[6]; + senseBuffer = new byte[32]; + bool sense; + + cdb[0] = (byte)ScsiCommands.ReadBlockLimits; + + lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, out sense); + error = lastError != 0; + + DicConsole.DebugWriteLine("SCSI Device", "READ BLOCK LIMITS took {0} ms.", duration); + + return sense; + } + + /// + /// Reports current reading/writing elements position on the medium + /// + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool ReadPosition(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + return ReadPosition(out buffer, out senseBuffer, SscPositionForms.Short, timeout, out duration); + } + + /// + /// Reports current reading/writing elements position on the medium using 32 bytes response + /// + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool ReadPositionLong(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + return ReadPosition(out buffer, out senseBuffer, SscPositionForms.Long, timeout, out duration); + } + + /// + /// Reports current reading/writing elements position on the medium + /// + /// Buffer. + /// Sense buffer. + /// Requests the position to be given in vendor-specified meaning. + /// Requests the response to be 32 bytes format. + /// Requests current logical position. + /// Timeout. + /// Duration. + public bool ReadPosition(out byte[] buffer, out byte[] senseBuffer, bool vendorType, bool longForm, bool totalPosition, uint timeout, out double duration) + { + byte responseForm = 0; + if (vendorType) + responseForm += 0x01; + if (longForm) + responseForm += 0x02; + if (totalPosition) + responseForm += 0x04; + + return ReadPosition(out buffer, out senseBuffer, (SscPositionForms)responseForm, timeout, out duration); + } + + /// + /// Reports current reading/writing elements position on the medium + /// + /// Buffer. + /// Sense buffer. + /// Response form. + /// Timeout. + /// Duration. + public bool ReadPosition(out byte[] buffer, out byte[] senseBuffer, SscPositionForms responseForm, uint timeout, out double duration) + { + switch (responseForm) + { + case SscPositionForms.Long: + case SscPositionForms.OldLong: + case SscPositionForms.OldLongTclpVendor: + case SscPositionForms.OldLongVendor: + case SscPositionForms.Extended: + buffer = new byte[32]; + break; + case SscPositionForms.OldTclp: + case SscPositionForms.OldTclpVendor: + case SscPositionForms.Short: + case SscPositionForms.VendorShort: + buffer = new byte[20]; + break; + default: + buffer = new byte[32]; // Invalid + break; + } + byte[] cdb = new byte[10]; + senseBuffer = new byte[32]; + bool sense; + + cdb[0] = (byte)ScsiCommands.ReadPosition; + cdb[1] = (byte)((byte)responseForm & 0x1F); + if(responseForm == SscPositionForms.Extended) + { + cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[8] = (byte)(buffer.Length & 0xFF); + } + + lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, out sense); + error = lastError != 0; + + DicConsole.DebugWriteLine("SCSI Device", "READ POSITION took {0} ms.", duration); + + return sense; + } + + /// + /// Reads the specified number of blocks from the medium, backwards + /// + /// Buffer. + /// Sense buffer. + /// How many blocks to read. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool ReadReverse6(out byte[] buffer, out byte[] senseBuffer, uint blocks, uint blockSize, uint timeout, out double duration) + { + return ReadReverse6(out buffer, out senseBuffer, false, false, true, blocks, blockSize, timeout, out duration); + } + + /// + /// Reads the specified number of bytes or of blocks from the medium, backwards + /// + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. + /// How many bytes to read. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool ReadReverse6(out byte[] buffer, out byte[] senseBuffer, bool sili, uint transferLen, uint blockSize, uint timeout, out double duration) + { + return ReadReverse6(out buffer, out senseBuffer, false, sili, false, transferLen, blockSize, timeout, out duration); + } + + /// + /// Reads the specified number of bytes or of blocks from the medium, backwards + /// + /// Buffer. + /// Sense buffer. + /// If set to true drive should un-reverse the blocks and bytes + /// If set to true suppress the incorrect-length indication. Cannot be set while is set also. + /// If set to true indicates how many blocks to read of a fixed size. + /// Transfer length in blocks or bytes depending of status. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool ReadReverse6(out byte[] buffer, out byte[] senseBuffer, bool byteOrder, bool sili, bool fixedLen, uint transferLen, uint blockSize, uint timeout, out double duration) + { + if (fixedLen) + buffer = new byte[blockSize * transferLen]; + else + buffer = new byte[transferLen]; + byte[] cdb = new byte[6]; + senseBuffer = new byte[32]; + bool sense; + + cdb[0] = (byte)ScsiCommands.ReadReverse; + if (fixedLen) + cdb[1] += 0x01; + if (sili) + cdb[1] += 0x02; + if (byteOrder) + cdb[1] += 0x04; + cdb[2] = (byte)((transferLen & 0xFF0000) >> 16); + cdb[3] = (byte)((transferLen & 0xFF00) >> 8); + cdb[4] = (byte)(transferLen & 0xFF); + + lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, out sense); + error = lastError != 0; + + DicConsole.DebugWriteLine("SCSI Device", "READ REVERSE (6) took {0} ms.", duration); + + return sense; + } + + /// + /// Reads a number of fixed-length blocks starting at specified object, backwards + /// + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. + /// Object identifier. + /// How many blocks to read. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool ReadReverse16(out byte[] buffer, out byte[] senseBuffer, bool sili, ulong objectId, uint blocks, uint blockSize, uint timeout, out double duration) + { + return ReadReverse16(out buffer, out senseBuffer, false, sili, false, 0, objectId, blocks, blockSize, timeout, out duration); + } + + /// + /// Reads a number of fixed-length blocks starting at specified block from the specified partition, backwards + /// + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. + /// Partition to read object from. + /// Object identifier. + /// How many blocks to read. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool ReadReverse16(out byte[] buffer, out byte[] senseBuffer, bool sili, byte partition, ulong objectId, uint blocks, uint blockSize, uint timeout, out double duration) + { + return ReadReverse16(out buffer, out senseBuffer, false, sili, false, partition, objectId, blocks, blockSize, timeout, out duration); + } + + /// + /// Reads a number of fixed-length blocks starting at specified object, backwards + /// + /// Buffer. + /// Sense buffer. + /// Object identifier. + /// How many blocks to read. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool ReadReverse16(out byte[] buffer, out byte[] senseBuffer, ulong objectId, uint blocks, uint blockSize, uint timeout, out double duration) + { + return ReadReverse16(out buffer, out senseBuffer, false, false, true, 0, objectId, blocks, blockSize, timeout, out duration); + } + + /// + /// Reads a number of fixed-length blocks starting at specified block from the specified partition, backwards + /// + /// Buffer. + /// Sense buffer. + /// Partition to read object from. + /// Object identifier. + /// How many blocks to read. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool ReadReverse16(out byte[] buffer, out byte[] senseBuffer, byte partition, ulong objectId, uint blocks, uint blockSize, uint timeout, out double duration) + { + return ReadReverse16(out buffer, out senseBuffer, false, false, true, partition, objectId, blocks, blockSize, timeout, out duration); + } + + /// + /// Reads a number of bytes or objects starting at specified object from the specified partition, backwards + /// + /// Buffer. + /// Sense buffer. + /// If set to true drive should un-reverse the blocks and bytes + /// If set to true suppress the incorrect-length indication. Cannot be set while is set also. + /// If set to true indicates how many blocks to read of a fixed size. + /// Partition to read object from. + /// Object identifier. + /// Transfer length in blocks or bytes depending of status. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool ReadReverse16(out byte[] buffer, out byte[] senseBuffer, bool byteOrder, bool sili, bool fixedLen, byte partition, ulong objectId, uint transferLen, uint objectSize, uint timeout, out double duration) + { + if (fixedLen) + buffer = new byte[objectSize * transferLen]; + else + buffer = new byte[transferLen]; + byte[] cdb = new byte[6]; + senseBuffer = new byte[32]; + bool sense; + byte[] idBytes = BitConverter.GetBytes(objectId); + + cdb[0] = (byte)ScsiCommands.Read16; + if (fixedLen) + cdb[1] += 0x01; + if (sili) + cdb[1] += 0x02; + if (byteOrder) + cdb[1] += 0x04; + cdb[3] = partition; + cdb[4] = idBytes[7]; + cdb[5] = idBytes[6]; + cdb[6] = idBytes[5]; + cdb[7] = idBytes[4]; + cdb[8] = idBytes[3]; + cdb[9] = idBytes[2]; + cdb[10] = idBytes[1]; + cdb[11] = idBytes[0]; + cdb[12] = (byte)((transferLen & 0xFF0000) >> 16); + cdb[13] = (byte)((transferLen & 0xFF00) >> 8); + cdb[14] = (byte)(transferLen & 0xFF); + + lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, out sense); + error = lastError != 0; + + DicConsole.DebugWriteLine("SCSI Device", "READ REVERSE (16) took {0} ms.", duration); + + return sense; + } + + /// + /// Reads the specified number of blocks from the device's buffer + /// + /// Buffer. + /// Sense buffer. + /// How many blocks to read. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool RecoverBufferedData(out byte[] buffer, out byte[] senseBuffer, uint blocks, uint blockSize, uint timeout, out double duration) + { + return RecoverBufferedData(out buffer, out senseBuffer, false, true, blocks, blockSize, timeout, out duration); + } + + /// + /// Reads the specified number of bytes or of blocks from the device's buffer + /// + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. + /// How many bytes to read. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool RecoverBufferedData(out byte[] buffer, out byte[] senseBuffer, bool sili, uint transferLen, uint blockSize, uint timeout, out double duration) + { + return RecoverBufferedData(out buffer, out senseBuffer, sili, false, transferLen, blockSize, timeout, out duration); + } + + /// + /// Reads the specified number of bytes or of blocks from the device's buffer + /// + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. Cannot be set while is set also. + /// If set to true indicates how many blocks to read of a fixed size. + /// Transfer length in blocks or bytes depending of status. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool RecoverBufferedData(out byte[] buffer, out byte[] senseBuffer, bool sili, bool fixedLen, uint transferLen, uint blockSize, uint timeout, out double duration) + { + if (fixedLen) + buffer = new byte[blockSize * transferLen]; + else + buffer = new byte[transferLen]; + byte[] cdb = new byte[6]; + senseBuffer = new byte[32]; + bool sense; + + cdb[0] = (byte)ScsiCommands.RecoverBufferedData; + if (fixedLen) + cdb[1] += 0x01; + if (sili) + cdb[1] += 0x02; + cdb[2] = (byte)((transferLen & 0xFF0000) >> 16); + cdb[3] = (byte)((transferLen & 0xFF00) >> 8); + cdb[4] = (byte)(transferLen & 0xFF); + + lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, out sense); + error = lastError != 0; + + DicConsole.DebugWriteLine("SCSI Device", "RECOVER BUFFERED DATA took {0} ms.", duration); + + return sense; + } + + /// + /// Requests the device to return descriptors for supported densities or medium types + /// + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool ReportDensitySupport(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + return ReportDensitySupport(out buffer, out senseBuffer, false, false, timeout, out duration); + } + + /// + /// Requests the device to return descriptors for supported densities or medium types + /// + /// Buffer. + /// Sense buffer. + /// If set to true descriptors should apply to currently inserted media. + /// Timeout. + /// Duration. + public bool ReportDensitySupport(out byte[] buffer, out byte[] senseBuffer, bool currentMedia, uint timeout, out double duration) + { + return ReportDensitySupport(out buffer, out senseBuffer, false, currentMedia, timeout, out duration); + } + + /// + /// Requests the device to return descriptors for supported densities or medium types + /// + /// Buffer. + /// Sense buffer. + /// If set to true descriptors should be about medium types. + /// If set to true descriptors should apply to currently inserted media. + /// Timeout. + /// Duration. + public bool ReportDensitySupport(out byte[] buffer, out byte[] senseBuffer, bool mediumType, bool currentMedia, uint timeout, out double duration) + { + buffer = new byte[256]; + byte[] cdb = new byte[10]; + senseBuffer = new byte[32]; + bool sense; + + cdb[0] = (byte)ScsiCommands.ReportDensitySupport; + if (mediumType) + cdb[1] += 0x01; + if (currentMedia) + cdb[1] += 0x02; + cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[8] = (byte)(buffer.Length & 0xFF); + + lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, out sense); + error = lastError != 0; + + if (sense) + return true; + + ushort availableLength = (ushort)(((int)buffer[0] << 8) + buffer[1] + 2); + buffer = new byte[availableLength]; + cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[8] = (byte)(buffer.Length & 0xFF); + senseBuffer = new byte[32]; + + lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, out sense); + error = lastError != 0; + + DicConsole.DebugWriteLine("SCSI Device", "REPORT DENSITY SUPPORT took {0} ms.", duration); + + return sense; + } + + /// + /// Positions the reading/writing element to the beginning of current partition + /// + /// Sense buffer. + /// Timeout. + /// Duration. + public bool Rewind(out byte[] senseBuffer, uint timeout, out double duration) + { + return Rewind(out senseBuffer, false, timeout, out duration); + } + + /// + /// Positions the reading/writing element to the beginning of current partition + /// + /// Sense buffer. + /// If set to true return from the command immediately. + /// Timeout. + /// Duration. + public bool Rewind(out byte[] senseBuffer, bool immediate, uint timeout, out double duration) + { + senseBuffer = new byte[32]; + byte[] cdb = new byte[6]; + byte[] buffer = new byte[0]; + bool sense; + + cdb[0] = (byte)ScsiCommands.Rewind; + if (immediate) + cdb[1] += 0x01; + + lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, out sense); + error = lastError != 0; + + DicConsole.DebugWriteLine("SCSI Device", "REWIND took {0} ms.", duration); + + return sense; + } + + /// + /// Selects the specified track + /// + /// true, if select was tracked, false otherwise. + /// Sense buffer. + /// Track. + /// Timeout. + /// Duration. + public bool TrackSelect(out byte[] senseBuffer, byte track, uint timeout, out double duration) + { + senseBuffer = new byte[32]; + byte[] cdb = new byte[6]; + byte[] buffer = new byte[0]; + bool sense; + + cdb[0] = (byte)ScsiCommands.TrackSelect; + cdb[5] = track; + + lastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, out sense); + error = lastError != 0; + + DicConsole.DebugWriteLine("SCSI Device", "TRACK SELECT took {0} ms.", duration); + + return sense; + } + } +} + diff --git a/DiscImageChef.Devices/DiscImageChef.Devices.csproj b/DiscImageChef.Devices/DiscImageChef.Devices.csproj index f409fdc2..484e7801 100644 --- a/DiscImageChef.Devices/DiscImageChef.Devices.csproj +++ b/DiscImageChef.Devices/DiscImageChef.Devices.csproj @@ -57,6 +57,8 @@ + + diff --git a/DiscImageChef.Devices/Enums.cs b/DiscImageChef.Devices/Enums.cs index 7ebf7bf5..ca28c663 100644 --- a/DiscImageChef.Devices/Enums.cs +++ b/DiscImageChef.Devices/Enums.cs @@ -1566,7 +1566,7 @@ namespace DiscImageChef.Devices /// Writes attribute values to medium auxiliary memory /// SPC-3 rev. 21b /// - WriteAttribute = 0x8C, + WriteAttribute = 0x8D, /// /// Writes to the device's buffer /// SCSI-2 X3T9.2/375R rev. 10l @@ -3239,5 +3239,93 @@ namespace DiscImageChef.Devices /// SpeedRead = 0xBB } + + public enum SscLogicalIdTypes : byte + { + /// + /// Logical object identifier + /// + ObjectId = 0, + /// + /// Logical file identifier + /// + FileId = 1, + /// + /// Logical set identifier + /// + SetId = 2, + /// + /// Reserved + /// + Reserved = 3 + } + + public enum SscPositionForms : byte + { + /// + /// 20 bytes using logical block addresses + /// + Short = 0, + /// + /// 20 bytes using vendor-specified values + /// + VendorShort = 1, + /// + /// Equivalent to on SSC-1 + /// + OldLong = 2, + /// + /// Invalid: Equivalent to LONG + BT on SSC-1 + /// + OldLongVendor = 3, + /// + /// Invalid: Equivalent to TCLP on SSC-1 + /// + OldTclp = 4, + /// + /// Invalid: Equivalent to TCLP + BT on SSC-1 + /// + OldTclpVendor = 5, + /// + /// 32 bytes + /// + Long = 6, + /// + /// Invalid: Equivalent to TCLP + LONG + BT on SSC-1 + /// + OldLongTclpVendor = 7, + /// + /// From 28 bytes to allocation length + /// + Extended = 8 + } + + public enum ScsiAttributeAction : byte + { + /// + /// Return attribute values + /// + Values = 0, + /// + /// Return a list of available attributes + /// + List = 1, + /// + /// Returns a list of known logical volume numbers + /// + VolumeList = 2, + /// + /// Returns a list of known partition numbers + /// + PartitionList = 3, + /// + /// Returns a list of elements containing volumes with MAM + /// + ElementList = 4, + /// + /// Returns a list of supported attribute identifiers + /// + Supported = 5 + } }