diff --git a/DiscImageChef.CommonTypes.csproj b/DiscImageChef.CommonTypes.csproj index 6d28d29..4464c72 100644 --- a/DiscImageChef.CommonTypes.csproj +++ b/DiscImageChef.CommonTypes.csproj @@ -97,6 +97,10 @@ + + + + @@ -115,7 +119,8 @@ - + + diff --git a/Metadata/DeviceReport.cs b/Metadata/DeviceReport.cs index 6f00e06..12ab15e 100644 --- a/Metadata/DeviceReport.cs +++ b/Metadata/DeviceReport.cs @@ -39,9 +39,9 @@ using System; using System.ComponentModel; using System.Xml.Serialization; -using DiscImageChef.Decoders.ATA; -using DiscImageChef.Decoders.SCSI; -using DiscImageChef.Decoders.SCSI.MMC; +using DiscImageChef.CommonTypes.Structs.Devices.ATA; +using DiscImageChef.CommonTypes.Structs.Devices.SCSI; +using DiscImageChef.CommonTypes.Structs.Devices.SCSI.Modes; using Newtonsoft.Json; // ReSharper disable InconsistentNaming @@ -484,57 +484,57 @@ namespace DiscImageChef.CommonTypes.Metadata public class mmcModeType { - public bool AccurateCDDA { get; set; } - public bool BCK { get; set; } - public ushort BufferSize { get; set; } - public bool BufferUnderRunProtection { get; set; } - public bool CanEject { get; set; } - public bool CanLockMedia { get; set; } - public bool CDDACommand { get; set; } - public bool CompositeAudioVideo { get; set; } - public bool CSSandCPPMSupported { get; set; } - public ushort CurrentSpeed { get; set; } - public ushort CurrentWriteSpeed { get; set; } - public ushort CurrentWriteSpeedSelected { get; set; } - public bool DeterministicSlotChanger { get; set; } - public bool DigitalPort1 { get; set; } - public bool DigitalPort2 { get; set; } - public bool LeadInPW { get; set; } - public byte LoadingMechanismType { get; set; } - public bool LockStatus { get; set; } - public bool LSBF { get; set; } - public ushort MaximumSpeed { get; set; } - public ushort MaximumWriteSpeed { get; set; } - public bool PlaysAudio { get; set; } - public bool PreventJumperStatus { get; set; } - public bool RCK { get; set; } - public bool ReadsBarcode { get; set; } - public bool ReadsBothSides { get; set; } - public bool ReadsCDR { get; set; } - public bool ReadsCDRW { get; set; } - public bool ReadsDeinterlavedSubchannel { get; set; } - public bool ReadsDVDR { get; set; } - public bool ReadsDVDRAM { get; set; } - public bool ReadsDVDROM { get; set; } - public bool ReadsISRC { get; set; } - public bool ReadsMode2Form2 { get; set; } - public bool ReadsMode2Form1 { get; set; } - public bool ReadsPacketCDR { get; set; } - public bool ReadsSubchannel { get; set; } - public bool ReadsUPC { get; set; } - public bool ReturnsC2Pointers { get; set; } - public byte RotationControlSelected { get; set; } - public bool SeparateChannelMute { get; set; } - public bool SeparateChannelVolume { get; set; } - public bool SSS { get; set; } - public bool SupportsMultiSession { get; set; } - public ushort SupportedVolumeLevels { get; set; } - public bool TestWrite { get; set; } - public bool WritesCDR { get; set; } - public bool WritesCDRW { get; set; } - public bool WritesDVDR { get; set; } - public bool WritesDVDRAM { get; set; } - public Modes.ModePage_2A_WriteDescriptor[] WriteSpeedPerformanceDescriptors { get; set; } + public bool AccurateCDDA { get; set; } + public bool BCK { get; set; } + public ushort BufferSize { get; set; } + public bool BufferUnderRunProtection { get; set; } + public bool CanEject { get; set; } + public bool CanLockMedia { get; set; } + public bool CDDACommand { get; set; } + public bool CompositeAudioVideo { get; set; } + public bool CSSandCPPMSupported { get; set; } + public ushort CurrentSpeed { get; set; } + public ushort CurrentWriteSpeed { get; set; } + public ushort CurrentWriteSpeedSelected { get; set; } + public bool DeterministicSlotChanger { get; set; } + public bool DigitalPort1 { get; set; } + public bool DigitalPort2 { get; set; } + public bool LeadInPW { get; set; } + public byte LoadingMechanismType { get; set; } + public bool LockStatus { get; set; } + public bool LSBF { get; set; } + public ushort MaximumSpeed { get; set; } + public ushort MaximumWriteSpeed { get; set; } + public bool PlaysAudio { get; set; } + public bool PreventJumperStatus { get; set; } + public bool RCK { get; set; } + public bool ReadsBarcode { get; set; } + public bool ReadsBothSides { get; set; } + public bool ReadsCDR { get; set; } + public bool ReadsCDRW { get; set; } + public bool ReadsDeinterlavedSubchannel { get; set; } + public bool ReadsDVDR { get; set; } + public bool ReadsDVDRAM { get; set; } + public bool ReadsDVDROM { get; set; } + public bool ReadsISRC { get; set; } + public bool ReadsMode2Form2 { get; set; } + public bool ReadsMode2Form1 { get; set; } + public bool ReadsPacketCDR { get; set; } + public bool ReadsSubchannel { get; set; } + public bool ReadsUPC { get; set; } + public bool ReturnsC2Pointers { get; set; } + public byte RotationControlSelected { get; set; } + public bool SeparateChannelMute { get; set; } + public bool SeparateChannelVolume { get; set; } + public bool SSS { get; set; } + public bool SupportsMultiSession { get; set; } + public ushort SupportedVolumeLevels { get; set; } + public bool TestWrite { get; set; } + public bool WritesCDR { get; set; } + public bool WritesCDRW { get; set; } + public bool WritesDVDR { get; set; } + public bool WritesDVDRAM { get; set; } + public ModePage_2A_WriteDescriptor[] WriteSpeedPerformanceDescriptors { get; set; } [XmlIgnore] public bool MaximumSpeedSpecified { get; set; } diff --git a/Metadata/DeviceReportV2.cs b/Metadata/DeviceReportV2.cs index 8051563..b047bf1 100644 --- a/Metadata/DeviceReportV2.cs +++ b/Metadata/DeviceReportV2.cs @@ -43,9 +43,9 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using DiscImageChef.CommonTypes.Enums; -using DiscImageChef.Decoders.ATA; -using DiscImageChef.Decoders.SCSI; -using DiscImageChef.Decoders.SCSI.MMC; +using DiscImageChef.CommonTypes.Structs.Devices.ATA; +using DiscImageChef.CommonTypes.Structs.Devices.SCSI; +using DiscImageChef.CommonTypes.Structs.Devices.SCSI.Modes; using Newtonsoft.Json; // ReSharper disable VirtualMemberNeverOverridden.Global @@ -474,10 +474,10 @@ namespace DiscImageChef.CommonTypes.Metadata if(ata.WRVSectorCountMode2Specified) identifyDevice.WRVSectorCountMode2 = ata.WRVSectorCountMode2; - Identify = Decoders.ATA.Identify.Encode(identifyDevice); + Identify = Structs.Devices.ATA.Identify.Encode(identifyDevice); } - public Identify.IdentifyDevice? IdentifyDevice => Decoders.ATA.Identify.Decode(Identify); + public Identify.IdentifyDevice? IdentifyDevice => Structs.Devices.ATA.Identify.Decode(Identify); [JsonIgnore] public int Id { get; set; } @@ -550,7 +550,7 @@ namespace DiscImageChef.CommonTypes.Metadata if(InquiryData != null) return; - var inq = new Inquiry.SCSIInquiry(); + var inq = new Inquiry(); if(scsi.Inquiry.ANSIVersionSpecified) inq.ANSIVersion = scsi.Inquiry.ANSIVersion; @@ -620,10 +620,10 @@ namespace DiscImageChef.CommonTypes.Metadata inq.WBus16 = scsi.Inquiry.WideBus16; inq.WBus32 = scsi.Inquiry.WideBus32; - InquiryData = Decoders.SCSI.Inquiry.Encode(inq); + InquiryData = Structs.Devices.SCSI.Inquiry.Encode(inq); } - public Inquiry.SCSIInquiry? Inquiry => Decoders.SCSI.Inquiry.Decode(InquiryData); + public Inquiry? Inquiry => Structs.Devices.SCSI.Inquiry.Decode(InquiryData); [JsonIgnore] public int Id { get; set; } @@ -766,7 +766,7 @@ namespace DiscImageChef.CommonTypes.Metadata public Mmc(mmcType mmc) { if(mmc.ModeSense2A != null) - ModeSense2AData = Modes.EncodeModePage_2A(new Modes.ModePage_2A + ModeSense2AData = ModePage_2A.Encode(new ModePage_2A { AccurateCDDA = mmc.ModeSense2A.AccurateCDDA, BCK = mmc.ModeSense2A.BCK, BufferSize = mmc.ModeSense2A.BufferSize, @@ -833,7 +833,7 @@ namespace DiscImageChef.CommonTypes.Metadata [JsonIgnore] public int Id { get; set; } - public virtual Modes.ModePage_2A ModeSense2A => Modes.DecodeModePage_2A(ModeSense2AData); + public virtual ModePage_2A ModeSense2A => ModePage_2A.Decode(ModeSense2AData); public virtual MmcFeatures Features { get; set; } public virtual List TestedMedia { get; set; } public byte[] ModeSense2AData { get; set; } diff --git a/Structs/Devices/ATA/Identify.cs b/Structs/Devices/ATA/Identify.cs new file mode 100644 index 0000000..11cdf48 --- /dev/null +++ b/Structs/Devices/ATA/Identify.cs @@ -0,0 +1,1036 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Text; +using DiscImageChef.Console; +using Marshal = DiscImageChef.Helpers.Marshal; + +namespace DiscImageChef.CommonTypes.Structs.Devices.ATA +{ + /// + /// Information from following standards: T10-791D rev. 4c (ATA) T10-948D rev. 4c (ATA-2) T13-1153D rev. 18 + /// (ATA/ATAPI-4) T13-1321D rev. 3 (ATA/ATAPI-5) T13-1410D rev. 3b (ATA/ATAPI-6) T13-1532D rev. 4b (ATA/ATAPI-7) + /// T13-1699D rev. 3f (ATA8-ACS) T13-1699D rev. 4a (ATA8-ACS) T13-2015D rev. 2 (ACS-2) T13-2161D rev. 5 (ACS-3) CF+ + /// & CF Specification rev. 1.4 (CFA) + /// + [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "MemberCanBeInternal"), + SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] + public static class Identify + { + /// Capabilities flag bits. + [Flags] + public enum CapabilitiesBit : ushort + { + /// ATAPI: Interleaved DMA supported + InterleavedDMA = 0x8000, + /// ATAPI: Command queueing supported + CommandQueue = 0x4000, + /// Standby timer values are standard + StandardStanbyTimer = 0x2000, + /// ATAPI: Overlap operation supported + OverlapOperation = 0x2000, + /// ATAPI: ATA software reset required Obsoleted in ATA/ATAPI-4 + RequiresATASoftReset = 0x1000, + /// IORDY is supported + IORDY = 0x0800, + /// IORDY can be disabled + CanDisableIORDY = 0x0400, + /// LBA is supported + LBASupport = 0x0200, + /// DMA is supported + DMASupport = 0x0100, + /// Vendor unique Obsoleted in ATA/ATAPI-4 + VendorBit7 = 0x0080, + /// Vendor unique Obsoleted in ATA/ATAPI-4 + VendorBit6 = 0x0040, + /// Vendor unique Obsoleted in ATA/ATAPI-4 + VendorBit5 = 0x0020, + /// Vendor unique Obsoleted in ATA/ATAPI-4 + VendorBit4 = 0x0010, + /// Vendor unique Obsoleted in ATA/ATAPI-4 + VendorBit3 = 0x0008, + /// Vendor unique Obsoleted in ATA/ATAPI-4 + VendorBit2 = 0x0004, + /// Long Physical Alignment setting bit 1 + PhysicalAlignment1 = 0x0002, + /// Long Physical Alignment setting bit 0 + PhysicalAlignment0 = 0x0001 + } + + /// More capabilities flag bits. + [Flags] + public enum CapabilitiesBit2 : ushort + { + /// MUST NOT be set + MustBeClear = 0x8000, + /// MUST be set + MustBeSet = 0x4000, Reserved13 = 0x2000, Reserved12 = 0x1000, Reserved11 = 0x0800, + Reserved10 = 0x0400, Reserved09 = 0x0200, Reserved08 = 0x0100, + Reserved07 = 0x0080, Reserved06 = 0x0040, Reserved05 = 0x0020, + Reserved04 = 0x0010, Reserved03 = 0x0008, Reserved02 = 0x0004, + Reserved01 = 0x0002, + /// Indicates a device specific minimum standby timer value + SpecificStandbyTimer = 0x0001 + } + + [Flags] + public enum CapabilitiesBit3 : byte + { + /// BLOCK ERASE EXT supported + BlockErase = 0x0080, + /// OVERWRITE EXT supported + Overwrite = 0x0040, + /// CRYPTO SCRAMBLE EXT supported + CryptoScramble = 0x0020, + /// Sanitize feature set is supported + Sanitize = 0x0010, + /// If unset, sanitize commands are specified by ACS-2 + SanitizeCommands = 0x0008, + /// SANITIZE ANTIFREEZE LOCK EXT is supported + SanitizeAntifreeze = 0x0004, Reserved01 = 0x0002, + /// Multiple logical sector setting is valid + MultipleValid = 0x0001 + } + + /// More capabilities flag bits. + [Flags] + public enum CommandSetBit : ushort + { + /// Already obsolete in ATA/ATAPI-4, reserved in ATA3 + Obsolete15 = 0x8000, + /// NOP is supported + Nop = 0x4000, + /// READ BUFFER is supported + ReadBuffer = 0x2000, + /// WRITE BUFFER is supported + WriteBuffer = 0x1000, + /// Already obsolete in ATA/ATAPI-4, reserved in ATA3 + Obsolete11 = 0x0800, + /// Host Protected Area is supported + HPA = 0x0400, + /// DEVICE RESET is supported + DeviceReset = 0x0200, + /// SERVICE interrupt is supported + Service = 0x0100, + /// Release is supported + Release = 0x0080, + /// Look-ahead is supported + LookAhead = 0x0040, + /// Write cache is supported + WriteCache = 0x0020, + /// PACKET command set is supported + Packet = 0x0010, + /// Power Management feature set is supported + PowerManagement = 0x0008, + /// Removable Media feature set is supported + RemovableMedia = 0x0004, + /// Security Mode feature set is supported + SecurityMode = 0x0002, + /// SMART feature set is supported + SMART = 0x0001 + } + + /// More capabilities flag bits. + [Flags] + public enum CommandSetBit2 : ushort + { + /// MUST NOT be set + MustBeClear = 0x8000, + /// MUST BE SET + MustBeSet = 0x4000, + /// FLUSH CACHE EXT supported + FlushCacheExt = 0x2000, + /// FLUSH CACHE supported + FlushCache = 0x1000, + /// Device Configuration Overlay feature set supported + DCO = 0x0800, + /// 48-bit LBA supported + LBA48 = 0x0400, + /// Automatic Acoustic Management supported + AAM = 0x0200, + /// SET MAX security extension supported + SetMax = 0x0100, + /// Address Offset Reserved Area Boot NCITS TR27:2001 + AddressOffsetReservedAreaBoot = 0x0080, + /// SET FEATURES required to spin-up + SetFeaturesRequired = 0x0040, + /// Power-Up in standby feature set supported + PowerUpInStandby = 0x0020, + /// Removable Media Status Notification feature set is supported + RemovableNotification = 0x0010, + /// Advanced Power Management feature set is supported + APM = 0x0008, + /// Compact Flash feature set is supported + CompactFlash = 0x0004, + /// READ DMA QUEUED and WRITE DMA QUEUED are supported + RWQueuedDMA = 0x0002, + /// DOWNLOAD MICROCODE is supported + DownloadMicrocode = 0x0001 + } + + /// More capabilities flag bits. + [Flags] + public enum CommandSetBit3 : ushort + { + /// MUST NOT be set + MustBeClear = 0x8000, + /// MUST BE SET + MustBeSet = 0x4000, + /// IDLE IMMEDIATE with UNLOAD FEATURE is supported + IdleImmediate = 0x2000, + /// Reserved for INCITS TR-37/2004 + Reserved12 = 0x1000, + /// Reserved for INCITS TR-37/2004 + Reserved11 = 0x0800, + /// URG bit is supported in WRITE STREAM DMA EXT and WRITE STREAM EXT + WriteURG = 0x0400, + /// URG bit is supported in READ STREAM DMA EXT and READ STREAM EXT + ReadURG = 0x0200, + /// 64-bit World Wide Name is supported + WWN = 0x0100, + /// WRITE DMA QUEUED FUA EXT is supported + FUAWriteQ = 0x0080, + /// WRITE DMA FUA EXT and WRITE MULTIPLE FUA EXT are supported + FUAWrite = 0x0040, + /// General Purpose Logging feature supported + GPL = 0x0020, + /// Sstreaming feature set is supported + Streaming = 0x0010, + /// Media Card Pass Through command set supported + MCPT = 0x0008, + /// Media serial number supported + MediaSerial = 0x0004, + /// SMART self-test supported + SMARTSelfTest = 0x0002, + /// SMART error logging supported + SMARTLog = 0x0001 + } + + /// More capabilities flag bits. + [Flags] + public enum CommandSetBit4 : ushort + { + /// MUST NOT be set + MustBeClear = 0x8000, + /// MUST be set + MustBeSet = 0x4000, Reserved13 = 0x2000, Reserved12 = 0x1000, Reserved11 = 0x0800, + Reserved10 = 0x0400, + /// DSN feature set is supported + DSN = 0x0200, + /// Accessible Max Address Configuration is supported + AMAC = 0x0100, + /// Extended Power Conditions is supported + ExtPowerCond = 0x0080, + /// Extended Status Reporting is supported + ExtStatusReport = 0x0040, + /// Free-fall Control feature set is supported + FreeFallControl = 0x0020, + /// Supports segmented feature in DOWNLOAD MICROCODE + SegmentedDownloadMicrocode = 0x0010, + /// READ/WRITE DMA EXT GPL are supported + RWDMAExtGpl = 0x0008, + /// WRITE UNCORRECTABLE is supported + WriteUnc = 0x0004, + /// Write/Read/Verify is supported + WRV = 0x0002, + /// Reserved for DT1825 + DT1825 = 0x0001 + } + + [Flags] + public enum CommandSetBit5 : ushort + { + /// Supports CFast Specification + CFast = 0x8000, + /// Deterministic read after TRIM is supported + DeterministicTrim = 0x4000, + /// Long physical sector alignment error reporting control is supported + LongPhysSectorAligError = 0x2000, + /// DEVICE CONFIGURATION IDENTIFY DMA and DEVICE CONFIGURATION SET DMA are supported + DeviceConfDMA = 0x1000, + /// READ BUFFER DMA is supported + ReadBufferDMA = 0x0800, + /// WRITE BUFFER DMA is supported + WriteBufferDMA = 0x0400, + /// SET PASSWORD DMA and SET UNLOCK DMA are supported + SetMaxDMA = 0x0200, + /// DOWNLOAD MICROCODE DMA is supported + DownloadMicroCodeDMA = 0x0100, + /// Reserved for IEEE-1667 + IEEE1667 = 0x0080, + /// Optional ATA 28-bit commands are supported + Ata28 = 0x0040, + /// Read zero after TRIM is supported + ReadZeroTrim = 0x0020, + /// Device encrypts all user data + Encrypted = 0x0010, + /// Extended number of user addressable sectors is supported + ExtSectors = 0x0008, + /// All write cache is non-volatile + AllCacheNV = 0x0004, + /// Zoned capabilities bit 1 + ZonedBit1 = 0x0002, + /// Zoned capabilities bit 0 + ZonedBit0 = 0x0001 + } + + [Flags] + public enum DataSetMgmtBit : ushort + { + Reserved15 = 0x8000, Reserved14 = 0x4000, Reserved13 = 0x2000, + Reserved12 = 0x1000, Reserved11 = 0x0800, Reserved10 = 0x0400, + Reserved09 = 0x0200, Reserved08 = 0x0100, Reserved07 = 0x0080, + Reserved06 = 0x0040, Reserved05 = 0x0020, Reserved04 = 0x0010, + Reserved03 = 0x0008, Reserved02 = 0x0004, Reserved01 = 0x0002, + /// TRIM is suported + Trim = 0x0001 + } + + public enum DeviceFormFactorEnum : ushort + { + /// Size not reported + NotReported = 0, + /// 5.25" + FiveAndQuarter = 1, + /// 3.5" + ThreeAndHalf = 2, + /// 2.5" + TwoAndHalf = 3, + /// 1.8" + OnePointEight = 4, + /// Less than 1.8" + LessThanOnePointEight = 5 + } + + /// Extended identify flag bits. + [Flags] + public enum ExtendedIdentifyBit : byte + { + /// Reserved + Reserved07 = 0x80, + /// Reserved + Reserved06 = 0x40, + /// Reserved + Reserved05 = 0x20, + /// Reserved + Reserved04 = 0x10, + /// Reserved + Reserved03 = 0x08, + /// Identify word 88 is valid + Word88Valid = 0x04, + /// Identify words 64 to 70 are valid + Words64to70Valid = 0x02, + /// Identify words 54 to 58 are valid + Words54to58Valid = 0x01 + } + + /// General configuration flag bits. + [Flags] + public enum GeneralConfigurationBit : ushort + { + /// Set on ATAPI + NonMagnetic = 0x8000, + /// Format speed tolerance gap is required Obsoleted in ATA-2 + FormatGapReq = 0x4000, + /// Track offset option is available Obsoleted in ATA-2 + TrackOffset = 0x2000, + /// Data strobe offset option is available Obsoleted in ATA-2 + DataStrobeOffset = 0x1000, + /// Rotational speed tolerance is higher than 0,5% Obsoleted in ATA-2 + RotationalSpeedTolerance = 0x0800, + /// Disk transfer rate is > 10 Mb/s Obsoleted in ATA-2 + UltraFastIDE = 0x0400, + /// Disk transfer rate is > 5 Mb/s but <= 10 Mb/s Obsoleted in ATA-2 + FastIDE = 0x0200, + /// Disk transfer rate is <= 5 Mb/s Obsoleted in ATA-2 + SlowIDE = 0x0100, + /// Drive uses removable media + Removable = 0x0080, + /// Drive is fixed Obsoleted in ATA/ATAPI-6 + Fixed = 0x0040, + /// Spindle motor control is implemented Obsoleted in ATA-2 + SpindleControl = 0x0020, + /// Head switch time is bigger than 15 µsec. Obsoleted in ATA-2 + HighHeadSwitch = 0x0010, + /// Drive is not MFM encoded Obsoleted in ATA-2 + NotMFM = 0x0008, + /// Drive is soft sectored Obsoleted in ATA-2 + SoftSector = 0x0004, + /// Response incomplete Since ATA/ATAPI-5 + IncompleteResponse = 0x0004, + /// Drive is hard sectored Obsoleted in ATA-2 + HardSector = 0x0002, + /// Reserved + Reserved = 0x0001 + } + + /// Word 80 Major version + [Flags] + public enum MajorVersionBit : ushort + { + Reserved15 = 0x8000, Reserved14 = 0x4000, Reserved13 = 0x2000, + Reserved12 = 0x1000, + /// ACS-4 + ACS4 = 0x0800, + /// ACS-3 + ACS3 = 0x0400, + /// ACS-2 + ACS2 = 0x0200, + /// ATA8-ACS + Ata8ACS = 0x0100, + /// ATA/ATAPI-7 + AtaAtapi7 = 0x0080, + /// ATA/ATAPI-6 + AtaAtapi6 = 0x0040, + /// ATA/ATAPI-5 + AtaAtapi5 = 0x0020, + /// ATA/ATAPI-4 + AtaAtapi4 = 0x0010, + /// ATA-3 + Ata3 = 0x0008, + /// ATA-2 + Ata2 = 0x0004, + /// ATA-1 + Ata1 = 0x0002, Reserved00 = 0x0001 + } + + [Flags] + public enum SATACapabilitiesBit : ushort + { + /// Supports READ LOG DMA EXT + ReadLogDMAExt = 0x8000, + /// Supports device automatic partial to slumber transitions + DevSlumbTrans = 0x4000, + /// Supports host automatic partial to slumber transitions + HostSlumbTrans = 0x2000, + /// Supports NCQ priroty + NCQPriority = 0x1000, + /// Supports unload while NCQ commands are outstanding + UnloadNCQ = 0x0800, + /// Supports PHY Event Counters + PHYEventCounter = 0x0400, + /// Supports receipt of host initiated power management requests + PowerReceipt = 0x0200, + /// Supports NCQ + NCQ = 0x0100, Reserved07 = 0x0080, Reserved06 = 0x0040, Reserved05 = 0x0020, + Reserved04 = 0x0010, + /// Supports SATA Gen. 3 Signaling Speed (6.0Gb/s) + Gen3Speed = 0x0008, + /// Supports SATA Gen. 2 Signaling Speed (3.0Gb/s) + Gen2Speed = 0x0004, + /// Supports SATA Gen. 1 Signaling Speed (1.5Gb/s) + Gen1Speed = 0x0002, + /// MUST NOT be set + Clear = 0x0001 + } + + [Flags] + public enum SATACapabilitiesBit2 : ushort + { + Reserved15 = 0x8000, Reserved14 = 0x4000, Reserved13 = 0x2000, + Reserved12 = 0x1000, Reserved11 = 0x0800, Reserved10 = 0x0400, + Reserved09 = 0x0200, Reserved08 = 0x0100, Reserved07 = 0x0080, + /// Supports RECEIVE FPDMA QUEUED and SEND FPDMA QUEUED + FPDMAQ = 0x0040, + /// Supports NCQ Queue Management + NCQMgmt = 0x0020, + /// ATAPI: Supports host environment detect + HostEnvDetect = 0x0020, + /// Supports NCQ streaming + NCQStream = 0x0010, + /// ATAPI: Supports device attention on slimline connected devices + DevAttSlimline = 0x0010, + /// Coded value indicating current negotiated Serial ATA signal speed + CurrentSpeedBit2 = 0x0008, + /// Coded value indicating current negotiated Serial ATA signal speed + CurrentSpeedBit1 = 0x0004, + /// Coded value indicating current negotiated Serial ATA signal speed + CurrentSpeedBit0 = 0x0002, + /// MUST NOT be set + Clear = 0x0001 + } + + [Flags] + public enum SATAFeaturesBit : ushort + { + Reserved15 = 0x8000, Reserved14 = 0x4000, Reserved13 = 0x2000, + Reserved12 = 0x1000, Reserved11 = 0x0800, Reserved10 = 0x0400, + Reserved09 = 0x0200, Reserved08 = 0x0100, + /// Supports NCQ autosense + NCQAutoSense = 0x0080, + /// Automatic Partial to Slumber transitions are enabled + EnabledSlumber = 0x0080, + /// Supports Software Settings Preservation + SettingsPreserve = 0x0040, + /// Supports hardware feature control + HardwareFeatureControl = 0x0020, + /// ATAPI: Asynchronous notification + AsyncNotification = 0x0020, + /// Supports in-order data delivery + InOrderData = 0x0010, + /// Supports initiating power management + InitPowerMgmt = 0x0008, + /// Supports DMA Setup auto-activation + DMASetup = 0x0004, + /// Supports non-zero buffer offsets + NonZeroBufferOffset = 0x0002, + /// MUST NOT be set + Clear = 0x0001 + } + + [Flags] + public enum SCTCommandTransportBit : ushort + { + Vendor15 = 0x8000, Vendor14 = 0x4000, Vendor13 = 0x2000, + Vendor12 = 0x1000, Reserved11 = 0x0800, Reserved10 = 0x0400, + Reserved09 = 0x0200, Reserved08 = 0x0100, Reserved07 = 0x0080, + Reserved06 = 0x0040, + /// SCT Command Transport Data Tables supported + DataTables = 0x0020, + /// SCT Command Transport Features Control supported + FeaturesControl = 0x0010, + /// SCT Command Transport Error Recovery Control supported + ErrorRecoveryControl = 0x0008, + /// SCT Command Transport Write Same supported + WriteSame = 0x0004, + /// SCT Command Transport Long Sector Address supported + LongSectorAccess = 0x0002, + /// SCT Command Transport supported + Supported = 0x0001 + } + + /// More capabilities flag bits. + [Flags] + public enum SecurityStatusBit : ushort + { + Reserved15 = 0x8000, Reserved14 = 0x4000, Reserved13 = 0x2000, + Reserved12 = 0x1000, Reserved11 = 0x0800, Reserved10 = 0x0400, + Reserved09 = 0x0200, + /// Maximum security level + Maximum = 0x0100, Reserved07 = 0x0080, Reserved06 = 0x0040, + /// Supports enhanced security erase + Enhanced = 0x0020, + /// Security count expired + Expired = 0x0010, + /// Security frozen + Frozen = 0x0008, + /// Security locked + Locked = 0x0004, + /// Security enabled + Enabled = 0x0002, + /// Security supported + Supported = 0x0001 + } + + public enum SpecificConfigurationEnum : ushort + { + /// Device requires SET FEATURES to spin up and IDENTIFY DEVICE response is incomplete + RequiresSetIncompleteResponse = 0x37C8, + /// Device requires SET FEATURES to spin up and IDENTIFY DEVICE response is complete + RequiresSetCompleteResponse = 0x738C, + /// Device does not requires SET FEATURES to spin up and IDENTIFY DEVICE response is incomplete + NotRequiresSetIncompleteResponse = 0x8C73, + /// Device does not requires SET FEATURES to spin up and IDENTIFY DEVICE response is complete + NotRequiresSetCompleteResponse = 0xC837 + } + + [Flags] + public enum TransferMode : byte + { + Mode7 = 0x80, Mode6 = 0x40, Mode5 = 0x20, + Mode4 = 0x10, Mode3 = 0x08, Mode2 = 0x04, + Mode1 = 0x02, Mode0 = 0x01 + } + + [Flags] + public enum TrustedComputingBit : ushort + { + /// MUST NOT be set + Clear = 0x8000, + /// MUST be set + Set = 0x4000, Reserved13 = 0x2000, Reserved12 = 0x1000, Reserved11 = 0x0800, + Reserved10 = 0x0400, Reserved09 = 0x0200, Reserved08 = 0x0100, + Reserved07 = 0x0080, Reserved06 = 0x0040, Reserved05 = 0x0020, + Reserved04 = 0x0010, Reserved03 = 0x0008, Reserved02 = 0x0004, + Reserved01 = 0x0002, + /// Trusted Computing feature set is supported + TrustedComputing = 0x0001 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 2)] + public struct IdentifyDevice + { + /// + /// Word 0 General device configuration On ATAPI devices: Bits 12 to 8 indicate device type as SCSI defined Bits 6 + /// to 5: 0 = Device shall set DRQ within 3 ms of receiving PACKET 1 = Device shall assert INTRQ when DRQ is set to one + /// 2 = Device shall set DRQ within 50 µs of receiving PACKET Bits 1 to 0: 0 = 12 byte command packet 1 = 16 byte + /// command packet CompactFlash is 0x848A (non magnetic, removable, not MFM, hardsector, and UltraFAST) + /// + public GeneralConfigurationBit GeneralConfiguration; + /// Word 1 Cylinders in default translation mode Obsoleted in ATA/ATAPI-6 + public ushort Cylinders; + /// Word 2 Specific configuration + public SpecificConfigurationEnum SpecificConfiguration; + /// Word 3 Heads in default translation mode Obsoleted in ATA/ATAPI-6 + public ushort Heads; + /// Word 4 Unformatted bytes per track in default translation mode Obsoleted in ATA-2 + public ushort UnformattedBPT; + /// Word 5 Unformatted bytes per sector in default translation mode Obsoleted in ATA-2 + public ushort UnformattedBPS; + /// Word 6 Sectors per track in default translation mode Obsoleted in ATA/ATAPI-6 + public ushort SectorsPerTrack; + /// Words 7 to 8 CFA: Number of sectors per card + public uint SectorsPerCard; + /// Word 9 Vendor unique Obsoleted in ATA/ATAPI-4 + public ushort VendorWord9; + /// Words 10 to 19 Device serial number, right justified, padded with spaces + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)] + public string SerialNumber; + /// + /// Word 20 Manufacturer defined Obsoleted in ATA-2 0x0001 = single ported single sector buffer 0x0002 = dual + /// ported multi sector buffer 0x0003 = dual ported multi sector buffer with reading + /// + public ushort BufferType; + /// Word 21 Size of buffer in 512 byte increments Obsoleted in ATA-2 + public ushort BufferSize; + /// Word 22 Bytes of ECC available in READ/WRITE LONG commands Obsoleted in ATA/ATAPI-4 + public ushort EccBytes; + /// Words 23 to 26 Firmware revision, left justified, padded with spaces + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] + public string FirmwareRevision; + /// Words 27 to 46 Model number, left justified, padded with spaces + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)] + public string Model; + /// + /// Word 47 bits 7 to 0 Maximum number of sectors that can be transferred per interrupt on read and write multiple + /// commands + /// + public byte MultipleMaxSectors; + /// Word 47 bits 15 to 8 Vendor unique ATA/ATAPI-4 says it must be 0x80 + public byte VendorWord47; + /// + /// Word 48 ATA-1: Set to 1 if it can perform doubleword I/O ATA-2 to ATA/ATAPI-7: Reserved ATA8-ACS: Trusted + /// Computing feature set + /// + public TrustedComputingBit TrustedComputing; + /// Word 49 Capabilities + public CapabilitiesBit Capabilities; + /// Word 50 Capabilities + public CapabilitiesBit2 Capabilities2; + /// Word 51 bits 7 to 0 Vendor unique Obsoleted in ATA/ATAPI-4 + public byte VendorWord51; + /// Word 51 bits 15 to 8 Transfer timing mode in PIO Obsoleted in ATA/ATAPI-4 + public byte PIOTransferTimingMode; + /// Word 52 bits 7 to 0 Vendor unique Obsoleted in ATA/ATAPI-4 + public byte VendorWord52; + /// Word 52 bits 15 to 8 Transfer timing mode in DMA Obsoleted in ATA/ATAPI-4 + public byte DMATransferTimingMode; + /// Word 53 bits 7 to 0 Reports if words 54 to 58 are valid + public ExtendedIdentifyBit ExtendedIdentify; + /// Word 53 bits 15 to 8 Free-fall Control Sensitivity + public byte FreeFallSensitivity; + /// Word 54 Cylinders in current translation mode Obsoleted in ATA/ATAPI-6 + public ushort CurrentCylinders; + /// Word 55 Heads in current translation mode Obsoleted in ATA/ATAPI-6 + public ushort CurrentHeads; + /// Word 56 Sectors per track in current translation mode Obsoleted in ATA/ATAPI-6 + public ushort CurrentSectorsPerTrack; + /// Words 57 to 58 Total sectors currently user-addressable Obsoleted in ATA/ATAPI-6 + public uint CurrentSectors; + /// Word 59 bits 7 to 0 Number of sectors currently set to transfer on a READ/WRITE MULTIPLE command + public byte MultipleSectorNumber; + /// Word 59 bits 15 to 8 Indicates if is valid + public CapabilitiesBit3 Capabilities3; + /// Words 60 to 61 If drive supports LBA, how many sectors are addressable using LBA + public uint LBASectors; + /// + /// Word 62 bits 7 to 0 Single word DMA modes available Obsoleted in ATA/ATAPI-4 In ATAPI it's not obsolete, + /// indicates UDMA mode (UDMA7 is instead MDMA0) + /// + public TransferMode DMASupported; + /// + /// Word 62 bits 15 to 8 Single word DMA mode currently active Obsoleted in ATA/ATAPI-4 In ATAPI it's not + /// obsolete, bits 0 and 1 indicate MDMA mode+1, bit 10 indicates DMA is supported and bit 15 indicates DMADIR bit in + /// PACKET is required for DMA transfers + /// + public TransferMode DMAActive; + /// Word 63 bits 7 to 0 Multiword DMA modes available + public TransferMode MDMASupported; + /// Word 63 bits 15 to 8 Multiword DMA mode currently active + public TransferMode MDMAActive; + + /// Word 64 bits 7 to 0 Supported Advanced PIO transfer modes + public TransferMode APIOSupported; + /// Word 64 bits 15 to 8 Reserved + public byte ReservedWord64; + /// Word 65 Minimum MDMA transfer cycle time per word in nanoseconds + public ushort MinMDMACycleTime; + /// Word 66 Recommended MDMA transfer cycle time per word in nanoseconds + public ushort RecMDMACycleTime; + /// Word 67 Minimum PIO transfer cycle time without flow control in nanoseconds + public ushort MinPIOCycleTimeNoFlow; + /// Word 68 Minimum PIO transfer cycle time with IORDY flow control in nanoseconds + public ushort MinPIOCycleTimeFlow; + + /// Word 69 Additional supported + public CommandSetBit5 CommandSet5; + /// Word 70 Reserved + public ushort ReservedWord70; + /// Word 71 ATAPI: Typical time in ns from receipt of PACKET to release bus + public ushort PacketBusRelease; + /// Word 72 ATAPI: Typical time in ns from receipt of SERVICE to clear BSY + public ushort ServiceBusyClear; + /// Word 73 Reserved + public ushort ReservedWord73; + /// Word 74 Reserved + public ushort ReservedWord74; + + /// Word 75 Maximum Queue depth + public ushort MaxQueueDepth; + + /// Word 76 Serial ATA Capabilities + public SATACapabilitiesBit SATACapabilities; + /// Word 77 Serial ATA Additional Capabilities + public SATACapabilitiesBit2 SATACapabilities2; + + /// Word 78 Supported Serial ATA features + public SATAFeaturesBit SATAFeatures; + /// Word 79 Enabled Serial ATA features + public SATAFeaturesBit EnabledSATAFeatures; + + /// Word 80 Major version of ATA/ATAPI standard supported + public MajorVersionBit MajorVersion; + /// Word 81 Minimum version of ATA/ATAPI standard supported + public ushort MinorVersion; + + /// Word 82 Supported command/feature sets + public CommandSetBit CommandSet; + /// Word 83 Supported command/feature sets + public CommandSetBit2 CommandSet2; + /// Word 84 Supported command/feature sets + public CommandSetBit3 CommandSet3; + + /// Word 85 Enabled command/feature sets + public CommandSetBit EnabledCommandSet; + /// Word 86 Enabled command/feature sets + public CommandSetBit2 EnabledCommandSet2; + /// Word 87 Enabled command/feature sets + public CommandSetBit3 EnabledCommandSet3; + + /// Word 88 bits 7 to 0 Supported Ultra DMA transfer modes + public TransferMode UDMASupported; + /// Word 88 bits 15 to 8 Selected Ultra DMA transfer modes + public TransferMode UDMAActive; + + /// Word 89 Time required for security erase completion + public ushort SecurityEraseTime; + /// Word 90 Time required for enhanced security erase completion + public ushort EnhancedSecurityEraseTime; + /// Word 91 Current advanced power management value + public ushort CurrentAPM; + + /// Word 92 Master password revision code + public ushort MasterPasswordRevisionCode; + /// Word 93 Hardware reset result + public ushort HardwareResetResult; + + /// Word 94 bits 7 to 0 Current AAM value + public byte CurrentAAM; + /// Word 94 bits 15 to 8 Vendor's recommended AAM value + public byte RecommendedAAM; + + /// Word 95 Stream minimum request size + public ushort StreamMinReqSize; + /// Word 96 Streaming transfer time in DMA + public ushort StreamTransferTimeDMA; + /// Word 97 Streaming access latency in DMA and PIO + public ushort StreamAccessLatency; + /// Words 98 to 99 Streaming performance granularity + public uint StreamPerformanceGranularity; + + /// Words 100 to 103 48-bit LBA addressable sectors + public ulong LBA48Sectors; + + /// Word 104 Streaming transfer time in PIO + public ushort StreamTransferTimePIO; + + /// Word 105 Maximum number of 512-byte block per DATA SET MANAGEMENT command + public ushort DataSetMgmtSize; + + /// + /// Word 106 Bit 15 should be zero Bit 14 should be one Bit 13 set indicates device has multiple logical sectors + /// per physical sector Bit 12 set indicates logical sector has more than 256 words (512 bytes) Bits 11 to 4 are + /// reserved Bits 3 to 0 indicate power of two of logical sectors per physical sector + /// + public ushort PhysLogSectorSize; + + /// Word 107 Interseek delay for ISO-7779 acoustic testing, in microseconds + public ushort InterseekDelay; + + /// Words 108 to 111 World Wide Name + public ulong WWN; + + /// Words 112 to 115 Reserved for WWN extension to 128 bit + public ulong WWNExtension; + + /// Word 116 Reserved for technical report + public ushort ReservedWord116; + + /// Words 117 to 118 Words per logical sector + public uint LogicalSectorWords; + + /// Word 119 Supported command/feature sets + public CommandSetBit4 CommandSet4; + /// Word 120 Supported command/feature sets + public CommandSetBit4 EnabledCommandSet4; + + /// Words 121 to 125 Reserved + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public ushort[] ReservedWords121; + + /// Word 126 ATAPI byte count limit + public ushort ATAPIByteCount; + + /// + /// Word 127 Removable Media Status Notification feature set support Bits 15 to 2 are reserved Bits 1 to 0 must be + /// 0 for not supported or 1 for supported. 2 and 3 are reserved. Obsoleted in ATA8-ACS + /// + public ushort RemovableStatusSet; + + /// Word 128 Security status + public SecurityStatusBit SecurityStatus; + + /// Words 129 to 159 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 31)] + public ushort[] ReservedWords129; + + /// + /// Word 160 CFA power mode Bit 15 must be set Bit 13 indicates mode 1 is required for one or more commands Bit 12 + /// indicates mode 1 is disabled Bits 11 to 0 indicates maximum current in mA + /// + public ushort CFAPowerMode; + + /// Words 161 to 167 Reserved for CFA + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public ushort[] ReservedCFA; + + /// Word 168 Bits 15 to 4, reserved Bits 3 to 0, device nominal form factor + public DeviceFormFactorEnum DeviceFormFactor; + /// Word 169 DATA SET MANAGEMENT support + public DataSetMgmtBit DataSetMgmt; + /// Words 170 to 173 Additional product identifier + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] + public string AdditionalPID; + + /// Word 174 Reserved + public ushort ReservedWord174; + /// Word 175 Reserved + public ushort ReservedWord175; + + /// Words 176 to 195 Current media serial number + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)] + public string MediaSerial; + /// Words 196 to 205 Current media manufacturer + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)] + public string MediaManufacturer; + + /// Word 206 SCT Command Transport features + public SCTCommandTransportBit SCTCommandTransport; + + /// Word 207 Reserved for CE-ATA + public ushort ReservedCEATAWord207; + /// Word 208 Reserved for CE-ATA + public ushort ReservedCEATAWord208; + + /// + /// Word 209 Alignment of logical block within a larger physical block Bit 15 shall be cleared to zero Bit 14 + /// shall be set to one Bits 13 to 0 indicate logical sector offset within the first physical sector + /// + public ushort LogicalAlignment; + + /// Words 210 to 211 Write/Read/Verify sector count mode 3 only + public uint WRVSectorCountMode3; + /// Words 212 to 213 Write/Read/Verify sector count mode 2 only + public uint WRVSectorCountMode2; + + /// + /// Word 214 NV Cache capabilities Bits 15 to 12 feature set version Bits 11 to 18 power mode feature set version + /// Bits 7 to 5 reserved Bit 4 feature set enabled Bits 3 to 2 reserved Bit 1 power mode feature set enabled Bit 0 + /// power mode feature set supported + /// + public ushort NVCacheCaps; + /// Words 215 to 216 NV Cache Size in Logical BLocks + public uint NVCacheSize; + /// Word 217 Nominal media rotation rate In ACS-1 meant NV Cache read speed in MB/s + public ushort NominalRotationRate; + /// Word 218 NV Cache write speed in MB/s Reserved since ACS-2 + public ushort NVCacheWriteSpeed; + /// Word 219 bits 7 to 0 Estimated device spin up in seconds + public byte NVEstimatedSpinUp; + /// Word 219 bits 15 to 8 NV Cache reserved + public byte NVReserved; + + /// Word 220 bits 7 to 0 Write/Read/Verify feature set current mode + public byte WRVMode; + /// Word 220 bits 15 to 8 Reserved + public byte WRVReserved; + + /// Word 221 Reserved + public ushort ReservedWord221; + + /// + /// Word 222 Transport major revision number Bits 15 to 12 indicate transport type. 0 parallel, 1 serial, 0xE + /// PCIe. Bits 11 to 0 indicate revision + /// + public ushort TransportMajorVersion; + /// Word 223 Transport minor revision number + public ushort TransportMinorVersion; + + /// Words 224 to 229 Reserved for CE-ATA + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public ushort[] ReservedCEATA224; + + /// Words 230 to 233 48-bit LBA if Word 69 bit 3 is set + public ulong ExtendedUserSectors; + + /// Word 234 Minimum number of 512 byte units per DOWNLOAD MICROCODE mode 3 + public ushort MinDownloadMicroMode3; + /// Word 235 Maximum number of 512 byte units per DOWNLOAD MICROCODE mode 3 + public ushort MaxDownloadMicroMode3; + + /// Words 236 to 254 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 19)] + public ushort[] ReservedWords; + + /// Word 255 bits 7 to 0 Should be 0xA5 + public byte Signature; + /// Word 255 bits 15 to 8 Checksum + public byte Checksum; + } + + public static IdentifyDevice? Decode(byte[] IdentifyDeviceResponse) + { + if(IdentifyDeviceResponse == null) + return null; + + if(IdentifyDeviceResponse.Length != 512) + { + DicConsole.DebugWriteLine("ATA/ATAPI IDENTIFY decoder", + "IDENTIFY response is different than 512 bytes, not decoding."); + + return null; + } + + IdentifyDevice ATAID = Marshal.ByteArrayToStructureLittleEndian(IdentifyDeviceResponse); + + ATAID.WWN = DescrambleWWN(ATAID.WWN); + ATAID.WWNExtension = DescrambleWWN(ATAID.WWNExtension); + + ATAID.SerialNumber = DescrambleATAString(IdentifyDeviceResponse, 10 * 2, 20); + ATAID.FirmwareRevision = DescrambleATAString(IdentifyDeviceResponse, 23 * 2, 8); + ATAID.Model = DescrambleATAString(IdentifyDeviceResponse, 27 * 2, 40); + ATAID.AdditionalPID = DescrambleATAString(IdentifyDeviceResponse, 170 * 2, 8); + ATAID.MediaSerial = DescrambleATAString(IdentifyDeviceResponse, 176 * 2, 40); + ATAID.MediaManufacturer = DescrambleATAString(IdentifyDeviceResponse, 196 * 2, 20); + + return ATAID; + } + + public static byte[] Encode(IdentifyDevice? identify) + { + if(identify is null) + return null; + + IdentifyDevice ataId = identify.Value; + + ataId.WWN = DescrambleWWN(ataId.WWN); + ataId.WWNExtension = DescrambleWWN(ataId.WWNExtension); + + byte[] buf = new byte[512]; + IntPtr ptr = System.Runtime.InteropServices.Marshal.AllocHGlobal(512); + System.Runtime.InteropServices.Marshal.StructureToPtr(ataId, ptr, false); + System.Runtime.InteropServices.Marshal.Copy(ptr, buf, 0, 512); + System.Runtime.InteropServices.Marshal.FreeHGlobal(ptr); + + byte[] str = ScrambleATAString(ataId.SerialNumber, 20); + Array.Copy(str, 0, buf, 10 * 2, 20); + str = ScrambleATAString(ataId.FirmwareRevision, 8); + Array.Copy(str, 0, buf, 23 * 2, 8); + str = ScrambleATAString(ataId.Model, 40); + Array.Copy(str, 0, buf, 27 * 2, 40); + str = ScrambleATAString(ataId.AdditionalPID, 8); + Array.Copy(str, 0, buf, 170 * 2, 8); + str = ScrambleATAString(ataId.MediaSerial, 40); + Array.Copy(str, 0, buf, 176 * 2, 40); + str = ScrambleATAString(ataId.MediaManufacturer, 20); + Array.Copy(str, 0, buf, 196 * 2, 20); + + return buf; + } + + static ulong DescrambleWWN(ulong WWN) + { + byte[] qwb = BitConverter.GetBytes(WWN); + byte[] qword = new byte[8]; + + qword[7] = qwb[1]; + qword[6] = qwb[0]; + qword[5] = qwb[3]; + qword[4] = qwb[2]; + qword[3] = qwb[5]; + qword[2] = qwb[4]; + qword[1] = qwb[7]; + qword[0] = qwb[6]; + + return BitConverter.ToUInt64(qword, 0); + } + + static string DescrambleATAString(IList buffer, int offset, int length) + { + byte[] outbuf = buffer[(offset + length) - 1] != 0x00 ? new byte[length + 1] : new byte[length]; + + for(int i = 0; i < length; i += 2) + { + outbuf[i] = buffer[offset + i + 1]; + outbuf[i + 1] = buffer[offset + i]; + } + + string outStr = StringHandlers.CToString(outbuf); + + return outStr.Trim(); + } + + static byte[] ScrambleATAString(string str, int length) + { + byte[] buf = new byte[length]; + + for(int i = 0; i < length; i++) + buf[i] = 0x20; + + if(str is null) + return buf; + + byte[] bytes = Encoding.ASCII.GetBytes(str); + + if(bytes.Length % 2 != 0) + { + byte[] tmp = new byte[bytes.Length + 1]; + tmp[tmp.Length - 1] = 0x20; + Array.Copy(bytes, 0, tmp, 0, bytes.Length); + bytes = tmp; + } + + for(int i = 0; i < bytes.Length; i += 2) + { + buf[i] = bytes[i + 1]; + buf[i + 1] = bytes[i]; + } + + string test1 = StringHandlers.CToString(buf); + string test2 = DescrambleATAString(buf, 0, length); + + return buf; + } + } +} \ No newline at end of file diff --git a/Structs/Devices/SCSI/Enums.cs b/Structs/Devices/SCSI/Enums.cs new file mode 100644 index 0000000..8b11310 --- /dev/null +++ b/Structs/Devices/SCSI/Enums.cs @@ -0,0 +1,227 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : Enums.cs +// Author(s) : Natalia Portillo +// +// Component : Device structures decoders. +// +// --[ Description ] ---------------------------------------------------------- +// +// Contains various SCSI enumerations. +// +// --[ License ] -------------------------------------------------------------- +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2020 Natalia Portillo +// ****************************************************************************/ + +using System.Diagnostics.CodeAnalysis; + +namespace DiscImageChef.CommonTypes.Structs.Devices.SCSI +{ + public enum PeripheralQualifiers : byte + { + /// Peripheral qualifier: Device is connected and supported + Supported = 0x00, + /// Peripheral qualifier: Device is supported but not connected + Unconnected = 0x01, + /// Peripheral qualifier: Reserved value + Reserved = 0x02, + /// Peripheral qualifier: Device is connected but unsupported + Unsupported = 0x03, + /// Peripheral qualifier: Vendor values: 0x04, 0x05, 0x06 and 0x07 + VendorMask = 0x04 + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum PeripheralDeviceTypes : byte + { + /// Direct-access device + DirectAccess = 0x00, + /// Sequential-access device + SequentialAccess = 0x01, + /// Printer device + PrinterDevice = 0x02, + /// Processor device + ProcessorDevice = 0x03, + /// Write-once device + WriteOnceDevice = 0x04, + /// CD-ROM/DVD/etc device + MultiMediaDevice = 0x05, + /// Scanner device + ScannerDevice = 0x06, + /// Optical memory device + OpticalDevice = 0x07, + /// Medium change device + MediumChangerDevice = 0x08, + /// Communications device + CommsDevice = 0x09, + /// Graphics arts pre-press device (defined in ASC IT8) + PrePressDevice1 = 0x0A, + /// Graphics arts pre-press device (defined in ASC IT8) + PrePressDevice2 = 0x0B, + /// Array controller device + ArrayControllerDevice = 0x0C, + /// Enclosure services device + EnclosureServiceDevice = 0x0D, + /// Simplified direct-access device + SimplifiedDevice = 0x0E, + /// Optical card reader/writer device + OCRWDevice = 0x0F, + /// Bridging Expanders + BridgingExpander = 0x10, + /// Object-based Storage Device + ObjectDevice = 0x11, + /// Automation/Drive Interface + ADCDevice = 0x12, + /// Security Manager Device + SCSISecurityManagerDevice = 0x13, + /// Host managed zoned block device + SCSIZonedBlockDevice = 0x14, + /// Well known logical unit + WellKnownDevice = 0x1E, + /// Unknown or no device type + UnknownDevice = 0x1F + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum ANSIVersions : byte + { + /// Device does not claim conformance to any ANSI version + ANSINoVersion = 0x00, + /// Device complies with ANSI X3.131:1986 + ANSI1986Version = 0x01, + /// Device complies with ANSI X3.131:1994 + ANSI1994Version = 0x02, + /// Device complies with ANSI X3.301:1997 + ANSI1997Version = 0x03, + /// Device complies with ANSI X3.351:2001 + ANSI2001Version = 0x04, + /// Device complies with ANSI X3.408:2005. + ANSI2005Version = 0x05, + /// Device complies with SPC-4 + ANSI2008Version = 0x06 + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum ECMAVersions : byte + { + /// Device does not claim conformance to any ECMA version + ECMANoVersion = 0x00, + /// Device complies with a ECMA-111 standard + ECMA111 = 0x01 + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum ISOVersions : byte + { + /// Device does not claim conformance to any ISO/IEC version + ISONoVersion = 0x00, + /// Device complies with ISO/IEC 9316:1995 + ISO1995Version = 0x02 + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum SPIClocking : byte + { + /// Supports only ST + ST = 0x00, + /// Supports only DT + DT = 0x01, + /// Reserved value + Reserved = 0x02, + /// Supports ST and DT + STandDT = 0x03 + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum TGPSValues : byte + { + /// Assymetrical access not supported + NotSupported = 0x00, + /// Only implicit assymetrical access is supported + OnlyImplicit = 0x01, + /// Only explicit assymetrical access is supported + OnlyExplicit = 0x02, + /// Both implicit and explicit assymetrical access are supported + Both = 0x03 + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum ProtocolIdentifiers : byte + { + /// Fibre Channel + FibreChannel = 0, + /// Parallel SCSI + SCSI = 1, + /// SSA + SSA = 2, + /// IEEE-1394 + Firewire = 3, + /// SCSI Remote Direct Memory Access Protocol + RDMAP = 4, + /// Internet SCSI + iSCSI = 5, + /// Serial SCSI + SAS = 6, + /// Automation/Drive Interface Transport Protocol + ADT = 7, + /// AT Attachment Interface (ATA/ATAPI) + ATA = 8, + /// USB Attached SCSI + UAS = 9, + /// SCSI over PCI Express + SCSIe = 10, + /// PCI Express + PCIe = 11, + /// No specific protocol + NoProtocol = 15 + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum ScsiDefinitions : byte + { + Current = 0, SCSI1 = 1, CCS = 2, + SCSI2 = 3, SCSI3 = 4 + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum PhysicalInterfaces : uint + { + /// Unspecified physical interface + Unspecified = 0, + /// SCSI + SCSI = 1, + /// ATAPI + ATAPI = 2, + /// IEEE-1394/1995 + IEEE1394 = 3, + /// IEEE-1394A + IEEE1394A = 4, + /// Fibre Channel + FC = 5, + /// IEEE-1394B + IEEE1394B = 6, + /// Serial ATAPI + SerialATAPI = 7, + /// USB + USB = 8, + /// Vendor unique + Vendor = 0xFFFF + } +} \ No newline at end of file diff --git a/Structs/Devices/SCSI/Inquiry.cs b/Structs/Devices/SCSI/Inquiry.cs new file mode 100644 index 0000000..5780017 --- /dev/null +++ b/Structs/Devices/SCSI/Inquiry.cs @@ -0,0 +1,706 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using DiscImageChef.Console; + +namespace DiscImageChef.CommonTypes.Structs.Devices.SCSI +{ + /// + /// Information from the following standards: T9/375-D revision 10l T10/995-D revision 10 T10/1236-D revision 20 + /// T10/1416-D revision 23 T10/1731-D revision 16 T10/502 revision 05 RFC 7144 ECMA-111 + /// + [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "MemberCanBeInternal"), + SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] + public struct Inquiry + { + /// Peripheral qualifier Byte 0, bits 7 to 5 + public byte PeripheralQualifier; + /// Peripheral device type Byte 0, bits 4 to 0 + public byte PeripheralDeviceType; + /// Removable device Byte 1, bit 7 + public bool RMB; + /// SCSI-1 vendor-specific qualification codes Byte 1, bits 6 to 0 + public byte DeviceTypeModifier; + /// ISO/IEC SCSI Standard Version Byte 2, bits 7 to 6, mask = 0xC0, >> 6 + public byte ISOVersion; + /// ECMA SCSI Standard Version Byte 2, bits 5 to 3, mask = 0x38, >> 3 + public byte ECMAVersion; + /// ANSI SCSI Standard Version Byte 2, bits 2 to 0, mask = 0x07 + public byte ANSIVersion; + /// Asynchronous Event Reporting Capability supported Byte 3, bit 7 + public bool AERC; + /// Device supports TERMINATE TASK command Byte 3, bit 6 + public bool TrmTsk; + /// Supports setting Normal ACA Byte 3, bit 5 + public bool NormACA; + /// Supports LUN hierarchical addressing Byte 3, bit 4 + public bool HiSup; + /// Responde data format Byte 3, bit 3 to 0 + public byte ResponseDataFormat; + /// Lenght of total INQUIRY response minus 4 Byte 4 + public byte AdditionalLength; + /// Device contains an embedded storage array controller Byte 5, bit 7 + public bool SCCS; + /// Device contains an Access Control Coordinator Byte 5, bit 6 + public bool ACC; + /// Supports asymetrical logical unit access Byte 5, bits 5 to 4 + public byte TPGS; + /// Supports third-party copy commands Byte 5, bit 3 + public bool ThreePC; + /// Reserved Byte 5, bits 2 to 1 + public byte Reserved2; + /// Supports protection information Byte 5, bit 0 + public bool Protect; + /// Supports basic queueing Byte 6, bit 7 + public bool BQue; + /// Device contains an embedded enclosure services component Byte 6, bit 6 + public bool EncServ; + /// Vendor-specific Byte 6, bit 5 + public bool VS1; + /// Multi-port device Byte 6, bit 4 + public bool MultiP; + /// Device contains or is attached to a medium changer Byte 6, bit 3 + public bool MChngr; + /// Device supports request and acknowledge handshakes Byte 6, bit 2 + public bool ACKREQQ; + /// Supports 32-bit wide SCSI addresses Byte 6, bit 1 + public bool Addr32; + /// Supports 16-bit wide SCSI addresses Byte 6, bit 0 + public bool Addr16; + /// Device supports relative addressing Byte 7, bit 7 + public bool RelAddr; + /// Supports 32-bit wide data transfers Byte 7, bit 6 + public bool WBus32; + /// Supports 16-bit wide data transfers Byte 7, bit 5 + public bool WBus16; + /// Supports synchronous data transfer Byte 7, bit 4 + public bool Sync; + /// Supports linked commands Byte 7, bit 3 + public bool Linked; + /// Supports CONTINUE TASK and TARGET TRANSFER DISABLE commands Byte 7, bit 2 + public bool TranDis; + /// Supports TCQ queue Byte 7, bit 1 + public bool CmdQue; + /// Indicates that the devices responds to RESET with soft reset Byte 7, bit 0 + public bool SftRe; + /// Vendor identification Bytes 8 to 15 + public byte[] VendorIdentification; + /// Product identification Bytes 16 to 31 + public byte[] ProductIdentification; + /// Product revision level Bytes 32 to 35 + public byte[] ProductRevisionLevel; + /// Vendor-specific data Bytes 36 to 55 + public byte[] VendorSpecific; + /// Byte 56, bits 7 to 4 + public byte Reserved3; + /// Supported SPI clocking Byte 56, bits 3 to 2 + public byte Clocking; + /// Device supports Quick Arbitration and Selection Byte 56, bit 1 + public bool QAS; + /// Supports information unit transfers Byte 56, bit 0 + public bool IUS; + /// Reserved Byte 57 + public byte Reserved4; + /// Array of version descriptors Bytes 58 to 73 + public ushort[] VersionDescriptors; + /// Reserved Bytes 74 to 95 + public byte[] Reserved5; + /// Reserved Bytes 96 to end + public byte[] VendorSpecific2; + + // Per DLT4000/DLT4500/DLT4700 Cartridge Tape Subsystem Product Manual + + #region Quantum vendor unique inquiry data structure + /// Means that the INQUIRY response contains 56 bytes or more, so this data has been filled + public bool QuantumPresent; + /// The product family. Byte 36, bits 7 to 5 + public byte Qt_ProductFamily; + /// The released firmware. Byte 36, bits 4 to 0 + public byte Qt_ReleasedFirmware; + /// The firmware major version. Byte 37 + public byte Qt_FirmwareMajorVersion; + /// The firmware minor version. Byte 38 + public byte Qt_FirmwareMinorVersion; + /// The EEPROM format major version. Byte 39 + public byte Qt_EEPROMFormatMajorVersion; + /// The EEPROM format minor version. Byte 40 + public byte Qt_EEPROMFormatMinorVersion; + /// The firmware personality. Byte 41 + public byte Qt_FirmwarePersonality; + /// The firmware sub personality. Byte 42 + public byte Qt_FirmwareSubPersonality; + /// The tape directory format version. Byte 43 + public byte Qt_TapeDirectoryFormatVersion; + /// The controller hardware version. Byte 44 + public byte Qt_ControllerHardwareVersion; + /// The drive EEPROM version. Byte 45 + public byte Qt_DriveEEPROMVersion; + /// The drive hardware version. Byte 46 + public byte Qt_DriveHardwareVersion; + /// The media loader firmware version. Byte 47 + public byte Qt_MediaLoaderFirmwareVersion; + /// The media loader hardware version. Byte 48 + public byte Qt_MediaLoaderHardwareVersion; + /// The media loader mechanical version. Byte 49 + public byte Qt_MediaLoaderMechanicalVersion; + /// Is a media loader present? Byte 50 + public bool Qt_MediaLoaderPresent; + /// Is a library present? Byte 51 + public bool Qt_LibraryPresent; + /// The module revision. Bytes 52 to 55 + public byte[] Qt_ModuleRevision; + #endregion Quantum vendor unique inquiry data structure + + #region IBM vendor unique inquiry data structure + /// Means that the INQUIRY response contains 56 bytes or more, so this data has been filled + public bool IBMPresent; + /// Drive is not capable of automation Byte 36 bit 0 + public bool IBM_AutDis; + /// If not zero, limit in MB/s = Max * (this / 256) Byte 37 + public byte IBM_PerformanceLimit; + /// Byte 41 + public byte IBM_OEMSpecific; + #endregion IBM vendor unique inquiry data structure + + #region HP vendor unique inquiry data structure + /// Means that the INQUIRY response contains 49 bytes or more, so this data has been filled + public bool HPPresent; + /// WORM version Byte 40 bits 7 to 1 + public byte HP_WORMVersion; + /// WORM supported Byte 40 bit 0 + public bool HP_WORM; + /// Bytes 43 to 48 + public byte[] HP_OBDR; + #endregion HP vendor unique inquiry data structure + + #region Seagate vendor unique inquiry data structure + /// Means that bytes 36 to 43 are filled + public bool SeagatePresent; + /// Drive Serial Number Bytes 36 to 43 + public byte[] Seagate_DriveSerialNumber; + /// Means that bytes 96 to 143 are filled + public bool Seagate2Present; + /// Contains Seagate copyright notice Bytes 96 to 143 + public byte[] Seagate_Copyright; + /// Means that bytes 144 to 147 are filled + public bool Seagate3Present; + /// Reserved Seagate field Bytes 144 to 147 + public byte[] Seagate_ServoPROMPartNo; + #endregion Seagate vendor unique inquiry data structure + + #region Kreon vendor unique inquiry data structure + /// Means that firmware is Kreon + public bool KreonPresent; + /// Kreon identifier Bytes 36 to 40 + public byte[] KreonIdentifier; + /// Kreon just a 0x20 Bytes 41 + public byte KreonSpace; + /// Kreon version string Bytes 42 to 46 + public byte[] KreonVersion; + #endregion Kreon vendor unique inquiry data structure + #region Public methods + public static Inquiry? Decode(byte[] SCSIInquiryResponse) + { + if(SCSIInquiryResponse == null) + return null; + + if(SCSIInquiryResponse.Length < 36 && + SCSIInquiryResponse.Length != 5) + { + DicConsole.DebugWriteLine("SCSI INQUIRY decoder", + "INQUIRY response is {0} bytes, less than minimum of 36 bytes, decoded data can be incorrect, not decoding.", + SCSIInquiryResponse.Length); + + return null; + } + + if(SCSIInquiryResponse.Length < SCSIInquiryResponse[4] + 4 && + SCSIInquiryResponse.Length != SCSIInquiryResponse[4]) + { + DicConsole.DebugWriteLine("SCSI INQUIRY decoder", + "INQUIRY response length ({0} bytes) is different than specified in length field ({1} bytes), decoded data can be incorrect, not decoding.", + SCSIInquiryResponse.Length, SCSIInquiryResponse[4] + 4); + + return null; + } + + var decoded = new Inquiry(); + + if(SCSIInquiryResponse.Length >= 1) + { + decoded.PeripheralQualifier = (byte)((SCSIInquiryResponse[0] & 0xE0) >> 5); + decoded.PeripheralDeviceType = (byte)(SCSIInquiryResponse[0] & 0x1F); + } + + if(SCSIInquiryResponse.Length >= 2) + { + decoded.RMB = Convert.ToBoolean(SCSIInquiryResponse[1] & 0x80); + decoded.DeviceTypeModifier = (byte)(SCSIInquiryResponse[1] & 0x7F); + } + + if(SCSIInquiryResponse.Length >= 3) + { + decoded.ISOVersion = (byte)((SCSIInquiryResponse[2] & 0xC0) >> 6); + decoded.ECMAVersion = (byte)((SCSIInquiryResponse[2] & 0x38) >> 3); + decoded.ANSIVersion = (byte)(SCSIInquiryResponse[2] & 0x07); + } + + if(SCSIInquiryResponse.Length >= 4) + { + decoded.AERC = Convert.ToBoolean(SCSIInquiryResponse[3] & 0x80); + decoded.TrmTsk = Convert.ToBoolean(SCSIInquiryResponse[3] & 0x40); + decoded.NormACA = Convert.ToBoolean(SCSIInquiryResponse[3] & 0x20); + decoded.HiSup = Convert.ToBoolean(SCSIInquiryResponse[3] & 0x10); + decoded.ResponseDataFormat = (byte)(SCSIInquiryResponse[3] & 0x07); + } + + if(SCSIInquiryResponse.Length >= 5) + decoded.AdditionalLength = SCSIInquiryResponse[4]; + + if(SCSIInquiryResponse.Length >= 6) + { + decoded.SCCS = Convert.ToBoolean(SCSIInquiryResponse[5] & 0x80); + decoded.ACC = Convert.ToBoolean(SCSIInquiryResponse[5] & 0x40); + decoded.TPGS = (byte)((SCSIInquiryResponse[5] & 0x30) >> 4); + decoded.ThreePC = Convert.ToBoolean(SCSIInquiryResponse[5] & 0x08); + decoded.Reserved2 = (byte)((SCSIInquiryResponse[5] & 0x06) >> 1); + decoded.Protect = Convert.ToBoolean(SCSIInquiryResponse[5] & 0x01); + } + + if(SCSIInquiryResponse.Length >= 7) + { + decoded.BQue = Convert.ToBoolean(SCSIInquiryResponse[6] & 0x80); + decoded.EncServ = Convert.ToBoolean(SCSIInquiryResponse[6] & 0x40); + decoded.VS1 = Convert.ToBoolean(SCSIInquiryResponse[6] & 0x20); + decoded.MultiP = Convert.ToBoolean(SCSIInquiryResponse[6] & 0x10); + decoded.MChngr = Convert.ToBoolean(SCSIInquiryResponse[6] & 0x08); + decoded.ACKREQQ = Convert.ToBoolean(SCSIInquiryResponse[6] & 0x04); + decoded.Addr32 = Convert.ToBoolean(SCSIInquiryResponse[6] & 0x02); + decoded.Addr16 = Convert.ToBoolean(SCSIInquiryResponse[6] & 0x01); + } + + if(SCSIInquiryResponse.Length >= 8) + { + decoded.RelAddr = Convert.ToBoolean(SCSIInquiryResponse[7] & 0x80); + decoded.WBus32 = Convert.ToBoolean(SCSIInquiryResponse[7] & 0x40); + decoded.WBus16 = Convert.ToBoolean(SCSIInquiryResponse[7] & 0x20); + decoded.Sync = Convert.ToBoolean(SCSIInquiryResponse[7] & 0x10); + decoded.Linked = Convert.ToBoolean(SCSIInquiryResponse[7] & 0x08); + decoded.TranDis = Convert.ToBoolean(SCSIInquiryResponse[7] & 0x04); + decoded.CmdQue = Convert.ToBoolean(SCSIInquiryResponse[7] & 0x02); + decoded.SftRe = Convert.ToBoolean(SCSIInquiryResponse[7] & 0x01); + } + + if(SCSIInquiryResponse.Length >= 16) + { + decoded.VendorIdentification = new byte[8]; + Array.Copy(SCSIInquiryResponse, 8, decoded.VendorIdentification, 0, 8); + } + + if(SCSIInquiryResponse.Length >= 32) + { + decoded.ProductIdentification = new byte[16]; + Array.Copy(SCSIInquiryResponse, 16, decoded.ProductIdentification, 0, 16); + } + + if(SCSIInquiryResponse.Length >= 36) + { + decoded.ProductRevisionLevel = new byte[4]; + Array.Copy(SCSIInquiryResponse, 32, decoded.ProductRevisionLevel, 0, 4); + } + + if(SCSIInquiryResponse.Length >= 44) + { + // Seagate 1 + decoded.SeagatePresent = true; + decoded.Seagate_DriveSerialNumber = new byte[8]; + Array.Copy(SCSIInquiryResponse, 36, decoded.Seagate_DriveSerialNumber, 0, 8); + } + + if(SCSIInquiryResponse.Length >= 46) + { + // Kreon + decoded.KreonIdentifier = new byte[5]; + Array.Copy(SCSIInquiryResponse, 36, decoded.KreonIdentifier, 0, 5); + decoded.KreonSpace = SCSIInquiryResponse[41]; + decoded.KreonVersion = new byte[5]; + Array.Copy(SCSIInquiryResponse, 42, decoded.KreonVersion, 0, 5); + + if(decoded.KreonSpace == 0x20 && + decoded.KreonIdentifier.SequenceEqual(new byte[] + { + 0x4B, 0x52, 0x45, 0x4F, 0x4E + })) + decoded.KreonPresent = true; + } + + if(SCSIInquiryResponse.Length >= 49) + { + // HP + decoded.HPPresent = true; + decoded.HP_WORM |= (SCSIInquiryResponse[40] & 0x01) == 0x01; + decoded.HP_WORMVersion = (byte)((SCSIInquiryResponse[40] & 0x7F) >> 1); + decoded.HP_OBDR = new byte[6]; + Array.Copy(SCSIInquiryResponse, 43, decoded.HP_OBDR, 0, 6); + } + + if(SCSIInquiryResponse.Length >= 56) + { + decoded.VendorSpecific = new byte[20]; + Array.Copy(SCSIInquiryResponse, 36, decoded.VendorSpecific, 0, 20); + + // Quantum + decoded.QuantumPresent = true; + decoded.Qt_ProductFamily = (byte)((SCSIInquiryResponse[36] & 0xF0) >> 4); + decoded.Qt_ReleasedFirmware = (byte)(SCSIInquiryResponse[36] & 0x0F); + decoded.Qt_FirmwareMajorVersion = SCSIInquiryResponse[37]; + decoded.Qt_FirmwareMinorVersion = SCSIInquiryResponse[38]; + decoded.Qt_EEPROMFormatMajorVersion = SCSIInquiryResponse[39]; + decoded.Qt_EEPROMFormatMinorVersion = SCSIInquiryResponse[40]; + decoded.Qt_FirmwarePersonality = SCSIInquiryResponse[41]; + decoded.Qt_FirmwareSubPersonality = SCSIInquiryResponse[42]; + decoded.Qt_TapeDirectoryFormatVersion = SCSIInquiryResponse[43]; + decoded.Qt_ControllerHardwareVersion = SCSIInquiryResponse[44]; + decoded.Qt_DriveEEPROMVersion = SCSIInquiryResponse[45]; + decoded.Qt_DriveHardwareVersion = SCSIInquiryResponse[46]; + decoded.Qt_MediaLoaderFirmwareVersion = SCSIInquiryResponse[47]; + decoded.Qt_MediaLoaderHardwareVersion = SCSIInquiryResponse[48]; + decoded.Qt_MediaLoaderMechanicalVersion = SCSIInquiryResponse[49]; + decoded.Qt_MediaLoaderPresent = SCSIInquiryResponse[50] > 0; + decoded.Qt_LibraryPresent = SCSIInquiryResponse[51] > 0; + decoded.Qt_ModuleRevision = new byte[4]; + Array.Copy(SCSIInquiryResponse, 52, decoded.Qt_ModuleRevision, 0, 4); + + // IBM + decoded.IBMPresent = true; + decoded.IBM_AutDis |= (SCSIInquiryResponse[36] & 0x01) == 0x01; + decoded.IBM_PerformanceLimit = SCSIInquiryResponse[37]; + decoded.IBM_OEMSpecific = SCSIInquiryResponse[41]; + } + + if(SCSIInquiryResponse.Length >= 57) + { + decoded.Reserved3 = (byte)((SCSIInquiryResponse[56] & 0xF0) >> 4); + decoded.Clocking = (byte)((SCSIInquiryResponse[56] & 0x0C) >> 2); + decoded.QAS = Convert.ToBoolean(SCSIInquiryResponse[56] & 0x02); + decoded.IUS = Convert.ToBoolean(SCSIInquiryResponse[56] & 0x01); + } + + if(SCSIInquiryResponse.Length >= 58) + decoded.Reserved4 = SCSIInquiryResponse[57]; + + if(SCSIInquiryResponse.Length >= 60) + { + int descriptorsNo; + + if(SCSIInquiryResponse.Length >= 74) + descriptorsNo = 8; + else + descriptorsNo = (SCSIInquiryResponse.Length - 58) / 2; + + decoded.VersionDescriptors = new ushort[descriptorsNo]; + + for(int i = 0; i < descriptorsNo; i++) + decoded.VersionDescriptors[i] = BitConverter.ToUInt16(SCSIInquiryResponse, 58 + (i * 2)); + } + + if(SCSIInquiryResponse.Length >= 75 && + SCSIInquiryResponse.Length < 96) + { + decoded.Reserved5 = new byte[SCSIInquiryResponse.Length - 74]; + Array.Copy(SCSIInquiryResponse, 74, decoded.Reserved5, 0, SCSIInquiryResponse.Length - 74); + } + + if(SCSIInquiryResponse.Length >= 96) + { + decoded.Reserved5 = new byte[22]; + Array.Copy(SCSIInquiryResponse, 74, decoded.Reserved5, 0, 22); + } + + if(SCSIInquiryResponse.Length > 96) + { + decoded.VendorSpecific2 = new byte[SCSIInquiryResponse.Length - 96]; + Array.Copy(SCSIInquiryResponse, 96, decoded.VendorSpecific2, 0, SCSIInquiryResponse.Length - 96); + } + + if(SCSIInquiryResponse.Length >= 144) + { + // Seagate 2 + decoded.Seagate2Present = true; + decoded.Seagate_Copyright = new byte[48]; + Array.Copy(SCSIInquiryResponse, 96, decoded.Seagate_Copyright, 0, 48); + } + + if(SCSIInquiryResponse.Length < 148) + return decoded; + + // Seagate 2 + decoded.Seagate3Present = true; + decoded.Seagate_ServoPROMPartNo = new byte[4]; + Array.Copy(SCSIInquiryResponse, 144, decoded.Seagate_ServoPROMPartNo, 0, 4); + + return decoded; + } + + public static byte[] Encode(Inquiry? inq) + { + if(inq is null) + return null; + + Inquiry decoded = inq.Value; + + byte[] buffer = new byte[512]; + byte length = 0; + + buffer[0] = (byte)(decoded.PeripheralQualifier << 5); + buffer[0] += decoded.PeripheralDeviceType; + + if(decoded.RMB) + buffer[1] = 0x80; + + buffer[1] += decoded.DeviceTypeModifier; + + buffer[2] = (byte)(decoded.ISOVersion << 6); + buffer[2] += (byte)(decoded.ECMAVersion << 3); + buffer[2] += decoded.ANSIVersion; + + if(decoded.AERC) + buffer[3] = 0x80; + + if(decoded.TrmTsk) + buffer[3] += 0x40; + + if(decoded.NormACA) + buffer[3] += 0x20; + + if(decoded.HiSup) + buffer[3] += 0x10; + + buffer[3] += decoded.ResponseDataFormat; + + if(decoded.AdditionalLength > 0) + { + length = 5; + buffer[4] = decoded.AdditionalLength; + } + + if(decoded.SCCS || + decoded.ACC || + decoded.TPGS > 0 || + decoded.ThreePC || + decoded.Reserved2 > 0 || + decoded.Protect) + { + length = 6; + + if(decoded.SCCS) + buffer[5] = 0x80; + + if(decoded.ACC) + buffer[5] += 0x40; + + buffer[5] += (byte)(decoded.TPGS << 4); + + if(decoded.ThreePC) + buffer[5] += 0x08; + + buffer[5] += (byte)(decoded.Reserved2 << 1); + + if(decoded.Protect) + buffer[5] += 0x01; + } + + if(decoded.BQue || + decoded.EncServ || + decoded.VS1 || + decoded.MultiP || + decoded.MChngr || + decoded.ACKREQQ || + decoded.Addr32 || + decoded.Addr16) + { + length = 7; + + if(decoded.BQue) + buffer[6] = 0x80; + + if(decoded.EncServ) + buffer[6] += 0x40; + + if(decoded.VS1) + buffer[6] += 0x20; + + if(decoded.MultiP) + buffer[6] += 0x10; + + if(decoded.MChngr) + buffer[6] += 0x08; + + if(decoded.ACKREQQ) + buffer[6] += 0x04; + + if(decoded.Addr32) + buffer[6] += 0x02; + + if(decoded.Addr16) + buffer[6] += 0x01; + } + + if(decoded.RelAddr || + decoded.WBus32 || + decoded.WBus16 || + decoded.Sync || + decoded.Linked || + decoded.TranDis || + decoded.CmdQue || + decoded.SftRe) + + { + length = 8; + + if(decoded.RelAddr) + buffer[7] = 0x80; + + if(decoded.WBus32) + buffer[7] += 0x40; + + if(decoded.WBus16) + buffer[7] += 0x20; + + if(decoded.Sync) + buffer[7] += 0x10; + + if(decoded.Linked) + buffer[7] += 0x08; + + if(decoded.TranDis) + buffer[7] += 0x04; + + if(decoded.CmdQue) + buffer[7] += 0x02; + + if(decoded.SftRe) + buffer[7] += 0x01; + } + + if(decoded.VendorIdentification != null) + { + length = 16; + + Array.Copy(decoded.VendorIdentification, 0, buffer, 8, + decoded.VendorIdentification.Length >= 8 ? 8 : decoded.VendorIdentification.Length); + } + + if(decoded.ProductIdentification != null) + { + length = 32; + + Array.Copy(decoded.ProductIdentification, 0, buffer, 16, + decoded.ProductIdentification.Length >= 16 ? 16 : decoded.ProductIdentification.Length); + } + + if(decoded.ProductRevisionLevel != null) + { + length = 36; + + Array.Copy(decoded.ProductRevisionLevel, 0, buffer, 32, + decoded.ProductRevisionLevel.Length >= 4 ? 4 : decoded.ProductRevisionLevel.Length); + } + + if(decoded.Seagate_DriveSerialNumber != null) + { + length = 44; + Array.Copy(decoded.Seagate_DriveSerialNumber, 0, buffer, 36, 8); + } + + if(decoded.KreonIdentifier != null && + decoded.KreonVersion != null) + { + length = 46; + Array.Copy(decoded.KreonIdentifier, 0, buffer, 36, 5); + buffer[41] = decoded.KreonSpace; + Array.Copy(decoded.KreonVersion, 0, buffer, 42, 5); + } + + if(decoded.HP_WORM || + decoded.HP_WORMVersion > 0 || + decoded.HP_OBDR != null) + { + length = 49; + + if(decoded.HP_WORM) + buffer[40] = 0x01; + + buffer[40] += (byte)(decoded.HP_WORMVersion << 1); + Array.Copy(decoded.HP_OBDR, 0, buffer, 43, 6); + } + + if(decoded.VendorSpecific != null) + { + length = 56; + Array.Copy(decoded.VendorSpecific, 0, buffer, 36, 20); + } + + if(decoded.Reserved3 > 0 || + decoded.Clocking > 0 || + decoded.QAS || + decoded.IUS) + { + length = 57; + buffer[56] = (byte)(decoded.Reserved3 << 4); + buffer[56] += (byte)(decoded.Clocking << 2); + + if(decoded.QAS) + buffer[56] += 0x02; + + if(decoded.IUS) + buffer[56] += 0x01; + } + + if(decoded.Reserved4 != 0) + { + length = 58; + buffer[57] = decoded.Reserved4; + } + + if(decoded.VersionDescriptors != null) + { + length = (byte)(58 + (decoded.VersionDescriptors.Length * 2)); + + for(int i = 0; i < decoded.VersionDescriptors.Length; i++) + Array.Copy(BitConverter.GetBytes(decoded.VersionDescriptors[i]), 0, buffer, 56 + (i * 2), 2); + } + + if(decoded.Reserved5 != null) + { + length = (byte)(74 + decoded.Reserved5.Length); + Array.Copy(decoded.Reserved5, 0, buffer, 74, decoded.Reserved5.Length); + } + + if(decoded.VendorSpecific2 != null) + { + length = (byte)(96 + decoded.VendorSpecific2.Length); + Array.Copy(decoded.VendorSpecific2, 0, buffer, 96, decoded.VendorSpecific2.Length); + } + + if(decoded.Seagate_Copyright != null) + { + length = 144; + Array.Copy(decoded.Seagate_Copyright, 0, buffer, 96, 48); + } + + if(decoded.Seagate_ServoPROMPartNo != null) + { + length = 148; + Array.Copy(decoded.Seagate_ServoPROMPartNo, 0, buffer, 144, 4); + } + + buffer[4] = length; + byte[] dest = new byte[length]; + Array.Copy(buffer, 0, dest, 0, length); + + return dest; + } + #endregion Public methods + } +} \ No newline at end of file diff --git a/Structs/Devices/SCSI/Modes/2A.cs b/Structs/Devices/SCSI/Modes/2A.cs new file mode 100644 index 0000000..e35876b --- /dev/null +++ b/Structs/Devices/SCSI/Modes/2A.cs @@ -0,0 +1,474 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : 2A.cs +// Author(s) : Natalia Portillo +// +// Component : Device structures decoders. +// +// --[ Description ] ---------------------------------------------------------- +// +// Decodes SCSI MODE PAGE 2Ah: CD-ROM capabilities page. +// +// --[ License ] -------------------------------------------------------------- +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2020 Natalia Portillo +// ****************************************************************************/ + +using System; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; + +namespace DiscImageChef.CommonTypes.Structs.Devices.SCSI.Modes +{ + [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "MemberCanBeInternal"), + SuppressMessage("ReSharper", "MemberCanBePrivate.Global"), SuppressMessage("ReSharper", "NotAccessedField.Global")] + #region Mode Page 0x2A: CD-ROM capabilities page + /// + /// CD-ROM capabilities page Page code 0x2A 16 bytes in OB-U0077C 20 bytes in SFF-8020i 22 bytes in MMC-1 26 bytes + /// in MMC-2 Variable bytes in MMC-3 + /// + public class ModePage_2A + { + public ModePage_2A_WriteDescriptor[] WriteSpeedPerformanceDescriptors; + /// Parameters can be saved + public bool PS { get; set; } + /// Drive supports multi-session and/or Photo-CD + public bool MultiSession { get; set; } + /// Drive is capable of reading sectors in Mode 2 Form 2 format + public bool Mode2Form2 { get; set; } + /// Drive is capable of reading sectors in Mode 2 Form 1 format + public bool Mode2Form1 { get; set; } + /// Drive is capable of playing audio + public bool AudioPlay { get; set; } + /// Drive can return the ISRC + public bool ISRC { get; set; } + /// Drive can return the media catalogue number + public bool UPC { get; set; } + /// Drive can return C2 pointers + public bool C2Pointer { get; set; } + /// Drive can read, deinterlave and correct R-W subchannels + public bool DeinterlaveSubchannel { get; set; } + /// Drive can read interleaved and uncorrected R-W subchannels + public bool Subchannel { get; set; } + /// Drive can continue from a loss of streaming on audio reading + public bool AccurateCDDA { get; set; } + /// Audio can be read as digital data + public bool CDDACommand { get; set; } + /// Loading Mechanism Type + public byte LoadingMechanism { get; set; } + /// Drive can eject discs + public bool Eject { get; set; } + /// Drive's optional prevent jumper status + public bool PreventJumper { get; set; } + /// Current lock status + public bool LockState { get; set; } + /// Drive can lock media + public bool Lock { get; set; } + /// Each channel can be muted independently + public bool SeparateChannelMute { get; set; } + /// Each channel's volume can be controlled independently + public bool SeparateChannelVolume { get; set; } + /// Maximum drive speed in Kbytes/second + public ushort MaximumSpeed { get; set; } + /// Supported volume levels + public ushort SupportedVolumeLevels { get; set; } + /// Buffer size in Kbytes + public ushort BufferSize { get; set; } + /// Current drive speed in Kbytes/second + public ushort CurrentSpeed { get; set; } + + public bool Method2 { get; set; } + public bool ReadCDRW { get; set; } + public bool ReadCDR { get; set; } + public bool WriteCDRW { get; set; } + public bool WriteCDR { get; set; } + public bool DigitalPort2 { get; set; } + public bool DigitalPort1 { get; set; } + public bool Composite { get; set; } + public bool SSS { get; set; } + public bool SDP { get; set; } + public byte Length { get; set; } + public bool LSBF { get; set; } + public bool RCK { get; set; } + public bool BCK { get; set; } + + public bool TestWrite { get; set; } + public ushort MaxWriteSpeed { get; set; } + public ushort CurrentWriteSpeed { get; set; } + + public bool ReadBarcode { get; set; } + + public bool ReadDVDRAM { get; set; } + public bool ReadDVDR { get; set; } + public bool ReadDVDROM { get; set; } + public bool WriteDVDRAM { get; set; } + public bool WriteDVDR { get; set; } + public bool LeadInPW { get; set; } + public bool SCC { get; set; } + public ushort CMRSupported { get; set; } + + public bool BUF { get; set; } + public byte RotationControlSelected { get; set; } + public ushort CurrentWriteSpeedSelected { get; set; } + + [JsonIgnore, Key] + public int Id { get; set; } + + public static ModePage_2A Decode(byte[] pageResponse) + { + if((pageResponse?[0] & 0x40) == 0x40) + return null; + + if((pageResponse?[0] & 0x3F) != 0x2A) + return null; + + if(pageResponse[1] + 2 != pageResponse.Length) + return null; + + if(pageResponse.Length < 16) + return null; + + var decoded = new ModePage_2A(); + + decoded.PS |= (pageResponse[0] & 0x80) == 0x80; + + decoded.AudioPlay |= (pageResponse[4] & 0x01) == 0x01; + decoded.Mode2Form1 |= (pageResponse[4] & 0x10) == 0x10; + decoded.Mode2Form2 |= (pageResponse[4] & 0x20) == 0x20; + decoded.MultiSession |= (pageResponse[4] & 0x40) == 0x40; + + decoded.CDDACommand |= (pageResponse[5] & 0x01) == 0x01; + decoded.AccurateCDDA |= (pageResponse[5] & 0x02) == 0x02; + decoded.Subchannel |= (pageResponse[5] & 0x04) == 0x04; + decoded.DeinterlaveSubchannel |= (pageResponse[5] & 0x08) == 0x08; + decoded.C2Pointer |= (pageResponse[5] & 0x10) == 0x10; + decoded.UPC |= (pageResponse[5] & 0x20) == 0x20; + decoded.ISRC |= (pageResponse[5] & 0x40) == 0x40; + + decoded.LoadingMechanism = (byte)((pageResponse[6] & 0xE0) >> 5); + decoded.Lock |= (pageResponse[6] & 0x01) == 0x01; + decoded.LockState |= (pageResponse[6] & 0x02) == 0x02; + decoded.PreventJumper |= (pageResponse[6] & 0x04) == 0x04; + decoded.Eject |= (pageResponse[6] & 0x08) == 0x08; + + decoded.SeparateChannelVolume |= (pageResponse[7] & 0x01) == 0x01; + decoded.SeparateChannelMute |= (pageResponse[7] & 0x02) == 0x02; + + decoded.MaximumSpeed = (ushort)((pageResponse[8] << 8) + pageResponse[9]); + decoded.SupportedVolumeLevels = (ushort)((pageResponse[10] << 8) + pageResponse[11]); + decoded.BufferSize = (ushort)((pageResponse[12] << 8) + pageResponse[13]); + decoded.CurrentSpeed = (ushort)((pageResponse[14] << 8) + pageResponse[15]); + + if(pageResponse.Length < 20) + return decoded; + + decoded.Method2 |= (pageResponse[2] & 0x04) == 0x04; + decoded.ReadCDRW |= (pageResponse[2] & 0x02) == 0x02; + decoded.ReadCDR |= (pageResponse[2] & 0x01) == 0x01; + + decoded.WriteCDRW |= (pageResponse[3] & 0x02) == 0x02; + decoded.WriteCDR |= (pageResponse[3] & 0x01) == 0x01; + + decoded.Composite |= (pageResponse[4] & 0x02) == 0x02; + decoded.DigitalPort1 |= (pageResponse[4] & 0x04) == 0x04; + decoded.DigitalPort2 |= (pageResponse[4] & 0x08) == 0x08; + + decoded.SDP |= (pageResponse[7] & 0x04) == 0x04; + decoded.SSS |= (pageResponse[7] & 0x08) == 0x08; + + decoded.Length = (byte)((pageResponse[17] & 0x30) >> 4); + decoded.LSBF |= (pageResponse[17] & 0x08) == 0x08; + decoded.RCK |= (pageResponse[17] & 0x04) == 0x04; + decoded.BCK |= (pageResponse[17] & 0x02) == 0x02; + + if(pageResponse.Length < 22) + return decoded; + + decoded.TestWrite |= (pageResponse[3] & 0x04) == 0x04; + decoded.MaxWriteSpeed = (ushort)((pageResponse[18] << 8) + pageResponse[19]); + decoded.CurrentWriteSpeed = (ushort)((pageResponse[20] << 8) + pageResponse[21]); + + decoded.ReadBarcode |= (pageResponse[5] & 0x80) == 0x80; + + if(pageResponse.Length < 26) + return decoded; + + decoded.ReadDVDRAM |= (pageResponse[2] & 0x20) == 0x20; + decoded.ReadDVDR |= (pageResponse[2] & 0x10) == 0x10; + decoded.ReadDVDROM |= (pageResponse[2] & 0x08) == 0x08; + + decoded.WriteDVDRAM |= (pageResponse[3] & 0x20) == 0x20; + decoded.WriteDVDR |= (pageResponse[3] & 0x10) == 0x10; + + decoded.LeadInPW |= (pageResponse[3] & 0x20) == 0x20; + decoded.SCC |= (pageResponse[3] & 0x10) == 0x10; + + decoded.CMRSupported = (ushort)((pageResponse[22] << 8) + pageResponse[23]); + + if(pageResponse.Length < 32) + return decoded; + + decoded.BUF |= (pageResponse[4] & 0x80) == 0x80; + decoded.RotationControlSelected = (byte)(pageResponse[27] & 0x03); + decoded.CurrentWriteSpeedSelected = (ushort)((pageResponse[28] << 8) + pageResponse[29]); + + ushort descriptors = (ushort)((pageResponse.Length - 32) / 4); + decoded.WriteSpeedPerformanceDescriptors = new ModePage_2A_WriteDescriptor[descriptors]; + + for(int i = 0; i < descriptors; i++) + decoded.WriteSpeedPerformanceDescriptors[i] = new ModePage_2A_WriteDescriptor + { + RotationControl = + (byte)(pageResponse[1 + 32 + (i * 4)] & 0x07), + WriteSpeed = (ushort)((pageResponse[2 + 32 + (i * 4)] << 8) + pageResponse[3 + 32 + (i * 4)]) + }; + + return decoded; + } + + public static byte[] Encode(ModePage_2A decoded) + { + byte[] pageResponse = new byte[512]; + byte length = 16; + + pageResponse[0] = 0x2A; + + if(decoded.PS) + pageResponse[0] += 0x80; + + if(decoded.AudioPlay) + pageResponse[4] += 0x01; + + if(decoded.Mode2Form1) + pageResponse[4] += 0x10; + + if(decoded.Mode2Form2) + pageResponse[4] += 0x20; + + if(decoded.MultiSession) + pageResponse[4] += 0x40; + + if(decoded.CDDACommand) + pageResponse[5] += 0x01; + + if(decoded.AccurateCDDA) + pageResponse[5] += 0x02; + + if(decoded.Subchannel) + pageResponse[5] += 0x04; + + if(decoded.DeinterlaveSubchannel) + pageResponse[5] += 0x08; + + if(decoded.C2Pointer) + pageResponse[5] += 0x10; + + if(decoded.UPC) + pageResponse[5] += 0x20; + + if(decoded.ISRC) + pageResponse[5] += 0x40; + + decoded.LoadingMechanism = (byte)((pageResponse[6] & 0xE0) >> 5); + + if(decoded.Lock) + pageResponse[6] += 0x01; + + if(decoded.LockState) + pageResponse[6] += 0x02; + + if(decoded.PreventJumper) + pageResponse[6] += 0x04; + + if(decoded.Eject) + pageResponse[6] += 0x08; + + if(decoded.SeparateChannelVolume) + pageResponse[7] += 0x01; + + if(decoded.SeparateChannelMute) + pageResponse[7] += 0x02; + + decoded.MaximumSpeed = (ushort)((pageResponse[8] << 8) + pageResponse[9]); + decoded.SupportedVolumeLevels = (ushort)((pageResponse[10] << 8) + pageResponse[11]); + decoded.BufferSize = (ushort)((pageResponse[12] << 8) + pageResponse[13]); + decoded.CurrentSpeed = (ushort)((pageResponse[14] << 8) + pageResponse[15]); + + if(decoded.Method2 || + decoded.ReadCDRW || + decoded.ReadCDR || + decoded.WriteCDRW || + decoded.WriteCDR || + decoded.Composite || + decoded.DigitalPort1 || + decoded.DigitalPort2 || + decoded.SDP || + decoded.SSS || + decoded.Length > 0 || + decoded.LSBF || + decoded.RCK || + decoded.BCK) + { + length = 20; + + if(decoded.Method2) + pageResponse[2] += 0x04; + + if(decoded.ReadCDRW) + pageResponse[2] += 0x02; + + if(decoded.ReadCDR) + pageResponse[2] += 0x01; + + if(decoded.WriteCDRW) + pageResponse[3] += 0x02; + + if(decoded.WriteCDR) + pageResponse[3] += 0x01; + + if(decoded.Composite) + pageResponse[4] += 0x02; + + if(decoded.DigitalPort1) + pageResponse[4] += 0x04; + + if(decoded.DigitalPort2) + pageResponse[4] += 0x08; + + if(decoded.SDP) + pageResponse[7] += 0x04; + + if(decoded.SSS) + pageResponse[7] += 0x08; + + pageResponse[17] = (byte)(decoded.Length << 4); + + if(decoded.LSBF) + pageResponse[17] += 0x08; + + if(decoded.RCK) + pageResponse[17] += 0x04; + + if(decoded.BCK) + pageResponse[17] += 0x02; + } + + if(decoded.TestWrite || + decoded.MaxWriteSpeed > 0 || + decoded.CurrentWriteSpeed > 0 || + decoded.ReadBarcode) + { + length = 22; + + if(decoded.TestWrite) + pageResponse[3] += 0x04; + + pageResponse[18] = (byte)((decoded.MaxWriteSpeed & 0xFF00) >> 8); + pageResponse[19] = (byte)(decoded.MaxWriteSpeed & 0xFF); + pageResponse[20] = (byte)((decoded.CurrentWriteSpeed & 0xFF00) >> 8); + pageResponse[21] = (byte)(decoded.CurrentWriteSpeed & 0xFF); + + if(decoded.ReadBarcode) + pageResponse[5] += 0x80; + } + + if(decoded.ReadDVDRAM || + decoded.ReadDVDR || + decoded.ReadDVDROM || + decoded.WriteDVDRAM || + decoded.WriteDVDR || + decoded.LeadInPW || + decoded.SCC || + decoded.CMRSupported > 0) + + { + length = 26; + + if(decoded.ReadDVDRAM) + pageResponse[2] += 0x20; + + if(decoded.ReadDVDR) + pageResponse[2] += 0x10; + + if(decoded.ReadDVDROM) + pageResponse[2] += 0x08; + + if(decoded.WriteDVDRAM) + pageResponse[3] += 0x20; + + if(decoded.WriteDVDR) + pageResponse[3] += 0x10; + + if(decoded.LeadInPW) + pageResponse[3] += 0x20; + + if(decoded.SCC) + pageResponse[3] += 0x10; + + pageResponse[22] = (byte)((decoded.CMRSupported & 0xFF00) >> 8); + pageResponse[23] = (byte)(decoded.CMRSupported & 0xFF); + } + + if(decoded.BUF || + decoded.RotationControlSelected > 0 || + decoded.CurrentWriteSpeedSelected > 0) + { + length = 32; + + if(decoded.BUF) + pageResponse[4] += 0x80; + + pageResponse[27] += decoded.RotationControlSelected; + pageResponse[28] = (byte)((decoded.CurrentWriteSpeedSelected & 0xFF00) >> 8); + pageResponse[29] = (byte)(decoded.CurrentWriteSpeedSelected & 0xFF); + } + + if(decoded.WriteSpeedPerformanceDescriptors != null) + { + length = 32; + + for(int i = 0; i < decoded.WriteSpeedPerformanceDescriptors.Length; i++) + { + length += 4; + pageResponse[1 + 32 + (i * 4)] = decoded.WriteSpeedPerformanceDescriptors[i].RotationControl; + + pageResponse[2 + 32 + (i * 4)] = + (byte)((decoded.WriteSpeedPerformanceDescriptors[i].WriteSpeed & 0xFF00) >> 8); + + pageResponse[3 + 32 + (i * 4)] = + (byte)(decoded.WriteSpeedPerformanceDescriptors[i].WriteSpeed & 0xFF); + } + } + + pageResponse[1] = (byte)(length - 2); + byte[] buf = new byte[length]; + Array.Copy(pageResponse, 0, buf, 0, length); + + return buf; + } + } + + public struct ModePage_2A_WriteDescriptor + { + public byte RotationControl; + public ushort WriteSpeed; + } + #endregion Mode Page 0x2A: CD-ROM capabilities page +} \ No newline at end of file