From 84ae9b91324f5a691ae335b2ff69d0e6b4c6c015 Mon Sep 17 00:00:00 2001 From: Konst Kolesnichenko Date: Tue, 6 Dec 2022 18:42:47 +0100 Subject: [PATCH 1/2] * Added iSCSI commands and enum values to support DVD-ROM devices --- ISCSI/SCSI/Enums/ModePageCodeName.cs | 1 + ISCSI/SCSI/Enums/SCSIOpCodeName.cs | 3 + .../MMC/GetConfigurationCommand.cs | 31 ++ .../MMC/ReadTocCommand.cs | 31 ++ .../ModeSense10CommandDescriptorBlock.cs | 65 ++++ .../SCSICommandDescriptorBlock.cs | 8 + .../MMC/GetConfigurationParameter.cs | 268 ++++++++++++++++ .../MMC/ReadDiscInformationParameter.cs | 34 ++ .../MMC/ReadTocParameter.cs | 301 ++++++++++++++++++ 9 files changed, 742 insertions(+) create mode 100644 ISCSI/SCSI/SCSICommandDescriptorBlock/MMC/GetConfigurationCommand.cs create mode 100644 ISCSI/SCSI/SCSICommandDescriptorBlock/MMC/ReadTocCommand.cs create mode 100644 ISCSI/SCSI/SCSICommandDescriptorBlock/ModeSense10CommandDescriptorBlock.cs create mode 100644 ISCSI/SCSI/SCSIReturnParameters/MMC/GetConfigurationParameter.cs create mode 100644 ISCSI/SCSI/SCSIReturnParameters/MMC/ReadDiscInformationParameter.cs create mode 100644 ISCSI/SCSI/SCSIReturnParameters/MMC/ReadTocParameter.cs diff --git a/ISCSI/SCSI/Enums/ModePageCodeName.cs b/ISCSI/SCSI/Enums/ModePageCodeName.cs index 8d96e63..610a59e 100644 --- a/ISCSI/SCSI/Enums/ModePageCodeName.cs +++ b/ISCSI/SCSI/Enums/ModePageCodeName.cs @@ -8,6 +8,7 @@ public enum ModePageCodeName : byte ControlModePage = 0x0A, PowerConditionModePage = 0x1A, InformationalExceptionsControlModePage = 0x1C, + MMCapabilitiesAndMechanicalStatus = 0x2A, ReturnAllPages = 0x3F, } } diff --git a/ISCSI/SCSI/Enums/SCSIOpCodeName.cs b/ISCSI/SCSI/Enums/SCSIOpCodeName.cs index f2fdc9b..79bf9ae 100644 --- a/ISCSI/SCSI/Enums/SCSIOpCodeName.cs +++ b/ISCSI/SCSI/Enums/SCSIOpCodeName.cs @@ -41,9 +41,12 @@ public enum SCSIOpCodeName : byte ReadLong10 = 0x3E, WriteLong10 = 0x3F, WriteSame10 = 0x41, + ReadToc = 0x43, ReportDensitySupport = 0x44, + GetConfiguration = 0x46, LogSelect10 = 0x4C, LogSense10 = 0x4D, + ReadDiscInformation = 0x51, ModeSelect10 = 0x55, ModeSense10 = 0x5A, PersistentReserveIn = 0x5E, diff --git a/ISCSI/SCSI/SCSICommandDescriptorBlock/MMC/GetConfigurationCommand.cs b/ISCSI/SCSI/SCSICommandDescriptorBlock/MMC/GetConfigurationCommand.cs new file mode 100644 index 0000000..0fd4ffc --- /dev/null +++ b/ISCSI/SCSI/SCSICommandDescriptorBlock/MMC/GetConfigurationCommand.cs @@ -0,0 +1,31 @@ +using System; +using Utilities; + +namespace SCSI +{ + internal class GetConfigurationCommand : SCSICommandDescriptorBlock + { + public const int PacketLength = 12; + // Request Type + public byte RT; + // Starting Feature Number + public ushort SFN; + public short AllocationLength; + + public GetConfigurationCommand() + { + OpCode = SCSIOpCodeName.GetConfiguration; + } + + public GetConfigurationCommand(byte[] buffer, int offset) + { + OpCode = (SCSIOpCodeName)buffer[offset + 0]; + RT = (byte)(buffer[offset +1] & 0x03); + SFN = BigEndianConverter.ToUInt16(buffer, offset + 2); + AllocationLength = BigEndianConverter.ToInt16(buffer, offset +7); + TransferLength = (uint)AllocationLength; + } + + public override byte[] GetBytes() => throw new NotImplementedException(); + } +} diff --git a/ISCSI/SCSI/SCSICommandDescriptorBlock/MMC/ReadTocCommand.cs b/ISCSI/SCSI/SCSICommandDescriptorBlock/MMC/ReadTocCommand.cs new file mode 100644 index 0000000..fb62197 --- /dev/null +++ b/ISCSI/SCSI/SCSICommandDescriptorBlock/MMC/ReadTocCommand.cs @@ -0,0 +1,31 @@ +using System; +using Utilities; + +namespace SCSI +{ + internal class ReadTocCommand : SCSICommandDescriptorBlock + { + public const int PacketLength = 12; + public bool MSF; + public byte Format; + public byte TrackSessionNumber; + public short AllocationLength; + + public ReadTocCommand() + { + OpCode = SCSIOpCodeName.ReadToc; + } + + public ReadTocCommand(byte[] buffer, int offset) + { + OpCode = (SCSIOpCodeName)buffer[offset + 0]; + MSF = (buffer[offset + 1] & 0x02) == 1; + Format = (byte)(buffer[offset + 2] & 0xF); + TrackSessionNumber = buffer[offset + 6]; + AllocationLength = BigEndianConverter.ToInt16(buffer, offset + 7); + TransferLength = (uint)AllocationLength; + } + + public override byte[] GetBytes() => throw new NotImplementedException(); + } +} diff --git a/ISCSI/SCSI/SCSICommandDescriptorBlock/ModeSense10CommandDescriptorBlock.cs b/ISCSI/SCSI/SCSICommandDescriptorBlock/ModeSense10CommandDescriptorBlock.cs new file mode 100644 index 0000000..352cd8b --- /dev/null +++ b/ISCSI/SCSI/SCSICommandDescriptorBlock/ModeSense10CommandDescriptorBlock.cs @@ -0,0 +1,65 @@ +/* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using Utilities; + +namespace SCSI +{ + public class ModeSense10CommandDescriptorBlock : SCSICommandDescriptorBlock + { + public const int PacketLength = 10; + + public bool DBD; // Disable block descriptors + public byte PC; // Page Control + public ModePageCodeName PageCode; + public byte SubpageCode; + public bool LLBA; + + public ModeSense10CommandDescriptorBlock() : base() + { + OpCode = SCSIOpCodeName.ModeSense10; + } + + public ModeSense10CommandDescriptorBlock(byte[] buffer, int offset) : base() + { + OpCode = (SCSIOpCodeName)buffer[offset + 0]; + DBD = (buffer[offset + 1] & 0x08) != 0; + PC = (byte)(buffer[offset + 2] >> 6); + PageCode = (ModePageCodeName)(buffer[offset + 2] & 0x3F); + SubpageCode = buffer[offset + 3]; + AllocationLength = BigEndianConverter.ToInt16(buffer,offset + 7); + Control = buffer[offset + 9]; + } + + public override byte[] GetBytes() + { + var buffer = new byte[PacketLength]; + buffer[0] = (byte)OpCode; + if (DBD) + { + buffer[1] |= 0x08; + } + buffer[2] |= (byte)(PC << 6); + buffer[2] |= (byte)((byte)PageCode & 0x3F); + buffer[3] = SubpageCode; + BigEndianWriter.WriteInt16(buffer, 7, AllocationLength); + buffer[9] = Control; + return buffer; + } + + public short AllocationLength + { + get + { + return (short)TransferLength; + } + set + { + TransferLength = (uint)value; + } + } + } +} diff --git a/ISCSI/SCSI/SCSICommandDescriptorBlock/SCSICommandDescriptorBlock.cs b/ISCSI/SCSI/SCSICommandDescriptorBlock/SCSICommandDescriptorBlock.cs index a7a3192..9d8fe7a 100644 --- a/ISCSI/SCSI/SCSICommandDescriptorBlock/SCSICommandDescriptorBlock.cs +++ b/ISCSI/SCSI/SCSICommandDescriptorBlock/SCSICommandDescriptorBlock.cs @@ -49,6 +49,8 @@ public static SCSICommandDescriptorBlock FromBytes(byte[] buffer, int offset) return new SCSICommandDescriptorBlock6(buffer, offset); case SCSIOpCodeName.ModeSense6: return new ModeSense6CommandDescriptorBlock(buffer, offset); + case SCSIOpCodeName.ModeSense10: + return new ModeSense10CommandDescriptorBlock(buffer, offset); case SCSIOpCodeName.ReadCapacity10: return new SCSICommandDescriptorBlock10(buffer, offset); case SCSIOpCodeName.Read10: @@ -73,6 +75,12 @@ public static SCSICommandDescriptorBlock FromBytes(byte[] buffer, int offset) return new SCSICommandDescriptorBlock16(buffer, offset); case SCSIOpCodeName.ReportLUNs: return new SCSICommandDescriptorBlock12(buffer, offset); + case SCSIOpCodeName.ReadDiscInformation: + return new SCSICommandDescriptorBlock12(buffer, offset); + case SCSIOpCodeName.ReadToc: + return new ReadTocCommand(buffer, offset); + case SCSIOpCodeName.GetConfiguration: + return new GetConfigurationCommand(buffer, offset); default: throw new UnsupportedSCSICommandException(String.Format("Unknown SCSI command: 0x{0}", opCode.ToString("x"))); } diff --git a/ISCSI/SCSI/SCSIReturnParameters/MMC/GetConfigurationParameter.cs b/ISCSI/SCSI/SCSIReturnParameters/MMC/GetConfigurationParameter.cs new file mode 100644 index 0000000..ff1c433 --- /dev/null +++ b/ISCSI/SCSI/SCSIReturnParameters/MMC/GetConfigurationParameter.cs @@ -0,0 +1,268 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Text; +using ISCSI; +using Utilities; + +namespace SCSI +{ + internal class GetConfigurationParameter + { + private const short MM_PROF_DVDROM = 0x0010; + private readonly Disk _disk; + private GetConfigurationCommand _cmd; + + public GetConfigurationParameter(Disk disk, GetConfigurationCommand command) + { + _disk = disk; + _cmd = command; + } + + internal byte[] GetBytes() + { + int hlen; + int len = 0; + int plen; + ushort fc; + + var data = new byte[128]; + + /* Reserved */ + data[4] = 0; + /* Reserved */ + data[5] = 0; + /* Current Profile */ + BigEndianWriter.WriteInt16(data, 6, MM_PROF_DVDROM); + + hlen = 8; + + switch (_cmd.RT) + { + case 0x00: + /* all of features */ + for (fc = _cmd.SFN; fc < 0xffff; fc++) + { + plen = istgt_lu_dvd_get_feature_descriptor(fc, data, hlen + len); + len += plen; + } + break; + + case 0x01: + /* current of features */ + for (fc = _cmd.SFN; fc < 0xffff; fc++) + { + plen = istgt_lu_dvd_get_feature_descriptor(fc, data, hlen + len); + if (data[hlen + 2] == 1) + { + len += plen; + } + else + { + /* drop non active descriptors */ + } + } + break; + + case 0x02: + /* specified feature */ + fc = _cmd.SFN; + plen = istgt_lu_dvd_get_feature_descriptor(fc, data, hlen + len); + len += plen; + break; + + default: + /* not supported */ + break; + } + + /* Data Length */ + BigEndianWriter.WriteInt32(data, 0, len); + + return data; + } + + private int istgt_lu_dvd_get_feature_descriptor(ushort fc, byte[] data, int offset) + { + byte hlen = 0, len = 0, plen; + + switch (fc) + { + case 0x0000: + /* Profile List */ + plen = 2 * 4 + 4; + FEATURE_DESCRIPTOR_INIT(data, offset, plen, fc); + /* Version(5-2) Persistent(1) Current(0) */ + // BDSET8W(&data[2], 0, 5, 4); + // BSET8(&data[2], 1); /* Persistent=1 */ + // BSET8(&data[2], 0); /* Current=1 */ + data[offset + 2] = 0b_0000_0011; + + hlen = 4; + + /* Profile Descriptor */ + /* Profile 1 (CDROM) */ + // DSET16(&cp[0], 0x0008); + BigEndianWriter.WriteInt16(data, offset + hlen + len, 0x0008); + + plen = 4; + len += plen; + + /* Profile 2 (DVDROM) */ + // DSET16(&cp[0], MM_PROF_DVDROM); + BigEndianWriter.WriteInt16(data, offset + hlen + len, MM_PROF_DVDROM); + +// BSET8(&cp[2], 0); /* CurrentP(0)=1 */ + data[offset + hlen + len + 2] = 1; + plen = 4; + len += plen; + break; + + case 0x0001: + /* Core Feature */ + /* GET CONFIGURATION/GET EVENT STATUS NOTIFICATION/INQUIRY */ + /* MODE SELECT (10)/MODE SENSE (10)/REQUEST SENSE/TEST UNIT READY */ + plen = 8 + 4; + FEATURE_DESCRIPTOR_INIT(data, offset, plen, fc); + /* Version(5-2) Persistent(1) Current(0) */ +// BDSET8W(&data[2], 0x01, 5, 4); /* MMC4 */ +// BSET8(&data[2], 1); /* Persistent=1 */ +// BSET8(&data[2], 0); /* Current=1 */ + data[offset + 2] = 0b_0000_0111; + hlen = 4; + + /* Physical Interface Standard */ + // DSET32(&data[4], 0x00000000); /* Unspecified */ + BigEndianWriter.WriteInt32(data, 4, 0x00000000); + /* DBE(0) */ + // BCLR8(&data[8], 0); /* DBE=0*/ + data[offset + 8] = 0; + len = 8; + break; + + case 0x0003: + /* Removable Medium */ + /* MECHANISM STATUS/PREVENT ALLOW MEDIUM REMOVAL/START STOP UNIT */ + plen = 0x04 + 4; + FEATURE_DESCRIPTOR_INIT(data, offset, plen, fc); + /* Version(5-2) Persistent(1) Current(0) */ +// BDSET8W(&data[2], 0x01, 5, 4); +// BSET8(&data[2], 1); /* Persistent=1 */ +// BSET8(&data[2], 0); /* Current=1 */ + data[offset + 2] = 0b_0000_0111; + hlen = 4; + + /* Loading Mechanism Type(7-5) Eject(3) Pvnt Jmpr(2) Lock(0) */ +// BDSET8W(&data[4], 0x01, 7, 3); /* Tray type loading mechanism */ +// BSET8(&data[4], 3); /* eject via START/STOP YES */ +// BSET8(&data[4], 0); /* locking YES */ + data[offset + 4] = 0b_0001_1001; + + len = 8; + break; + + case 0x0010: + /* Random Readable */ + /* READ CAPACITY/READ (10) */ + plen = 4 + 4; + FEATURE_DESCRIPTOR_INIT(data, offset, plen, fc); + /* Version(5-2) Persistent(1) Current(0) */ +// BDSET8W(&data[2], 0x00, 5, 4); +// BSET8(&data[2], 1); /* Persistent=1 */ +// BSET8(&data[2], 0); /* Current=1 */ + data[offset + 2] = 0b_0000_0011; + + hlen = 4; + + /* Logical Block Size */ + // DSET32(&data[4], (uint32_t)spec->blocklen); + BigEndianWriter.WriteInt32(data, 4, (int)_disk.TotalSectors); + /* Blocking */ + // DSET16(&data[8], 1); + BigEndianWriter.WriteUInt16(data, 8, 0x2); + /* PP(0) */ + // BCLR8(&data[10], 0); /* PP=0 */ + data[offset + 10] = 0; + len = 4; + break; + + case 0x001d: + /* Multi-Read Feature */ + /* READ (10)/READ CD/READ DISC INFORMATION/READ TRACK INFORMATION */ + plen = 4; + FEATURE_DESCRIPTOR_INIT(data, offset, plen, fc); + /* Version(5-2) Persistent(1) Current(0) */ +// BDSET8W(&data[2], 0x00, 5, 4); +// BSET8(&data[2], 1); /* Persistent=1 */ +// BSET8(&data[2], 0); /* Current=1 */ + data[offset + 2] = 0b_0000_0011; + + hlen = 4; + len = 0; + break; + + case 0x001e: + /* CD Read */ + /* READ CD/READ CD MSF/READ TOC/PMA/ATIP */ + plen = 4 + 4; + FEATURE_DESCRIPTOR_INIT(data, offset, plen, fc); + /* Version(5-2) Persistent(1) Current(0) */ +// BDSET8W(&data[2], 0x02, 5, 4); /* MMC4 */ +// BCLR8(&data[2], 1); /* Persistent=0 */ +// if (spec->profile == MM_PROF_CDROM) +// { +// BSET8(&data[2], 0); /* Current=1 */ +// } +// else +// { +// BCLR8(&data[2], 0); /* Current=0 */ +// } + data[offset + 2] = 0b_0000_1000; + + hlen = 4; + + /* DAP(7) C2 Flags(1) CD-Text(0) */ +// BCLR8(&data[4], 7); /* not support DAP */ +// BCLR8(&data[4], 1); /* not support C2 */ +// BCLR8(&data[4], 0); /* not support CD-Text */ + data[offset + 4] = 0; + len = 4; + break; + + case 0x001f: + /* DVD Read */ + /* READ (10)/READ (12)/READ DVD STRUCTURE/READ TOC/PMA/ATIP */ + plen = 4; + FEATURE_DESCRIPTOR_INIT(data, offset, plen, fc); + /* Version(5-2) Persistent(1) Current(0) */ +// BDSET8W(&data[2], 0x00, 5, 4); +// BCLR8(&data[2], 1); /* Persistent=0 */ +// if (spec->profile == MM_PROF_DVDROM) +// { +// BSET8(&data[2], 0); /* Current=1 */ +// } +// else +// { +// BCLR8(&data[2], 0); /* Current=0 */ +// } + data[offset + 2] = 0b_0000_0001; + + hlen = 4; + len = 0; + break; + + default: + /* not supported */ + break; + } + + return hlen + len; + } + + private void FEATURE_DESCRIPTOR_INIT(byte[] data, int offset, byte plen, ushort fc) + { + BigEndianWriter.WriteUInt16(data, offset, fc); + data[offset + 3] = (byte)(plen - 4); + } + } +} diff --git a/ISCSI/SCSI/SCSIReturnParameters/MMC/ReadDiscInformationParameter.cs b/ISCSI/SCSI/SCSIReturnParameters/MMC/ReadDiscInformationParameter.cs new file mode 100644 index 0000000..6ad42ca --- /dev/null +++ b/ISCSI/SCSI/SCSIReturnParameters/MMC/ReadDiscInformationParameter.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SCSI +{ + public class ReadDiscInformationParameter + { + internal byte[] GetBytes() + { + return new byte[] + { + 0x0, + 0x20, // size + 0xE, // 1110 (complete session | complete disc) + 0x1, // first track + (1) & 0xff, /* Number of Sessions (Least Significant Byte) */ + (1) & 0xff, /* First Track Number in Last Session (Least Significant Byte) */ + (1) & 0xff, /* Last Track Number in Last Session (Least Significant Byte) */ + 0xFC, // 1111 1100 + 0x00, // CD-DA or CD-ROM + (1 >> 8) & 0xff, /* Number of Sessions (Most Significant Byte) */ + (1 >> 8) & 0xff, /* First Track Number in Last Session (Most Significant Byte) */ + (1 >> 8) & 0xff, /* Last Track Number in Last Session (Most Significant Byte) */ + 0, 0, 0, 0, /* Disc Identification */ + 0, 0, 0, 0, /* Last Session Lead-in Start Address */ + 0, 0, 0, 0, /* Last Possible Lead-out Start Address */ + 0, 0, 0, 0, 0, 0, 0, 0, /* Disc Bar Code */ + 0, /* Disc Application Code */ + 0, /* Number of OPC Tables */ + }; + } + } +} diff --git a/ISCSI/SCSI/SCSIReturnParameters/MMC/ReadTocParameter.cs b/ISCSI/SCSI/SCSIReturnParameters/MMC/ReadTocParameter.cs new file mode 100644 index 0000000..9614316 --- /dev/null +++ b/ISCSI/SCSI/SCSIReturnParameters/MMC/ReadTocParameter.cs @@ -0,0 +1,301 @@ +using Utilities; + +namespace SCSI +{ + internal class ReadTocParameter + { + const int Length = 64; + private Disk _disk; + private bool _msf; + private readonly byte _format; + + public ReadTocParameter(Disk disk, bool msf, byte format) + { + _disk = disk; + _msf = msf; + _format = format; + } + + internal byte[] GetBytes() + { + switch(_format) + { + case 0: return GetFormattedTOC(); + case 1: return MultiSessionInformation(); + case 2: return RawTOC(); + default: return null; + } + } + + private byte[] RawTOC() + { + sbyte hlen; + sbyte len = 0, plen; + + byte[] data = new byte[Length]; + + /* First Complete Session Number */ + data[2] = 1; + /* Last Complete Session Number */ + data[3] = 1; + hlen = 4; + + /* TOC Track Descriptor */ + /* First Track number in the program area */ + int offset = hlen + len; + + /* Session Number */ + data[offset + 0] = 1; + /* ADR(7-4) CONTROL(3-0) */ + data[offset + 1] = 0x14; + /* TNO */ + data[offset + 2] = 0; + /* POINT */ + data[offset + 3] = 0xa0; + /* Min */ + data[offset + 4] = 0; + /* Sec */ + data[offset + 5] = 0; + /* Frame */ + data[offset + 6] = 0; + /* Zero */ + data[offset + 7] = 0; + /* PMIN / First Track Number */ + data[offset + 8] = 1; + /* PSEC / Disc Type */ + data[offset + 9] = 0x00; /* CD-DA or CD Data with first track in Mode 1 */ + /* PFRAME */ + data[offset + 10] = 0; + plen = 11; + len += plen; + + /* Last Track number in the program area */ + offset = hlen + len; + + /* Session Number */ + data[offset + 0] = 1; + /* ADR(7-4) CONTROL(3-0) */ + data[offset + 1] = 0x14; + /* TNO */ + data[offset + 2] = 0; + /* POINT */ + data[offset + 3] = 0xa1; + /* Min */ + data[offset + 4] = 0; + /* Sec */ + data[offset + 5] = 0; + /* Frame */ + data[offset + 6] = 0; + /* Zero */ + data[offset + 7] = 0; + /* PMIN / Last Track Number */ + data[offset + 8] = 1; + /* PSEC */ + data[offset + 9] = 0; + /* PFRAME */ + data[offset + 10] = 0; + plen = 11; + len += plen; + + /* Start location of the Lead-out area */ + offset = hlen + len; + + /* Session Number */ + data[offset + 0] = 1; + /* ADR(7-4) CONTROL(3-0) */ + data[offset + 1] = 0x14; + /* TNO */ + data[offset + 2] = 0; + /* POINT */ + data[offset + 3] = 0xa2; + /* Min */ + data[offset + 4] = 0; + /* Sec */ + data[offset + 5] = 0; + /* Frame */ + data[offset + 6] = 0; + /* Zero */ + data[offset + 7] = 0; + /* PMIN / Start position of Lead-out */ + /* PSEC / Start position of Lead-out */ + /* PFRAME / Start position of Lead-out */ + if (_msf) + { + // DSET24(&cp[8], istgt_lba2msf(spec->blockcnt)); + byte[] bytes = BigEndianConverter.GetBytes(_disk.TotalSectors); + data[offset + 8] = bytes[0]; + data[offset + 9] = bytes[1]; + data[offset + 10] = bytes[2]; + } + else + { + // DSET24(&data[offset + 8], spec->blockcnt); + byte[] bytes = BigEndianConverter.GetBytes(_disk.TotalSectors); + data[offset + 8] = bytes[0]; + data[offset + 9] = bytes[1]; + data[offset + 10] = bytes[2]; + } + plen = 11; + len += plen; + + /* Track data */ + offset = hlen + len; + + /* Session Number */ + data[offset + 0] = 1; + /* ADR(7-4) CONTROL(3-0) */ + data[offset + 1] = 0x14; + /* TNO */ + data[offset + 2] = 0; + /* POINT */ + data[offset + 3] = 1; + /* Min */ + data[offset + 4] = 0; + /* Sec */ + data[offset + 5] = 0; + /* Frame */ + data[offset + 6] = 0; + /* Zero */ + data[offset + 7] = 0; + /* PMIN / Start position of Lead-out */ + /* PSEC / Start position of Lead-out */ + /* PFRAME / Start position of Lead-out */ + if (_msf) + { + // TODO: add actual conversion if needed + // DSET24(&cp[8], istgt_lba2msf(0)); + BigEndianWriter.WriteInt16(data, offset + 8, 0); + } + else + { + // DSET24(&cp[8], 0); + BigEndianWriter.WriteInt16(data, offset + 8, 0); + } + plen = 11; + len += plen; + + /* TOC Data Length */ + // DSET16(&data[0], hlen + len - 2); + BigEndianWriter.WriteInt16(data, 0, (short)(hlen + len - 2)); + + return data; + } + private byte[] MultiSessionInformation() + { + sbyte hlen; + sbyte len = 0; + + byte[] data = new byte[Length]; + + /* First Complete Session Number */ + data[2] = 1; + /* Last Complete Session Number */ + data[3] = 1; + hlen = 4; + + /* TOC Track Descriptor */ + int offset = hlen + len; + + /* Reserved */ + data[offset + 0] = 0; + /* ADR(7-4) CONTROL(3-0) */ + data[offset + 1] = 0x14; + /* First Track Number In Last Complete Session */ + data[offset + 2] = 1; + /* Reserved */ + data[offset + 3] = 0; + /* Start Address of First Track in Last Session */ + if (_msf) + { + // TODO: add lba2msf if needed + // DSET32(&cp[4], istgt_lba2msf(0)); + BigEndianWriter.WriteInt32(data, offset + 4, /*lba2msf*/ 0); + } + else + { + BigEndianWriter.WriteInt32(data, offset + 4, 0); + } + len = 8; + + /* TOC Data Length */ + // DSET16(&data[0], hlen + len - 2); + BigEndianWriter.WriteInt16(data, 0, (short)(hlen + len - 2)); + + return data; + } + + internal byte[] GetFormattedTOC() + { + sbyte hlen; + sbyte len = 0, plen; + + byte[] data = new byte[Length]; + + /* First Track Number */ + data[2] = 1; + /* Last Track Number */ + data[3] = 1; + hlen = 4; + + /* TOC Track Descriptor */ + /* Track 1 Descriptor */ + int offset = hlen + len; + + /* Reserved */ + data[offset + 0] = 0; + /* ADR(7-4) CONTROL(3-0) */ + data[offset + 1] = 0x14; + /* Track Number */ + data[offset + 2] = 1; + /* Reserved */ + data[offset + 3] = 0; + + /* Track Start Address */ + if (_msf) + { + // DSET32(&cp[4], istgt_lba2msf(0)); + BigEndianWriter.WriteInt32(data, offset + 4, /*lba2msf*/ 0); + } + else + { + BigEndianWriter.WriteInt32(data, offset + 4, 0); + } + + plen = 8; + len += plen; + + /* Track AAh (Lead-out) Descriptor */ + // cp = &data[hlen + len]; + offset = hlen + len; + + /* Reserved */ + data[offset + 0] = 0; + /* ADR(7-4) CONTROL(3-0) */ + data[offset + 1] = 0x14; + /* Track Number */ + data[offset + 2] = 0xaa; + /* Reserved */ + data[offset + 3] = 0; + /* Track Start Address */ + if (_msf) + { + // DSET32(&cp[4], istgt_lba2msf(spec->blockcnt)); + BigEndianWriter.WriteInt32(data, offset + 4, /*lba2msf*/ (int)_disk.TotalSectors); + } + else + { + // DSET32(&cp[4], spec->blockcnt); + BigEndianWriter.WriteInt32(data, offset + 4, (int)_disk.TotalSectors); + } + + plen = 8; + len += plen; + + /* TOC Data Length */ + // DSET16(&data[0], hlen + len - 2) + BigEndianWriter.WriteInt16(data, 0, (short)(hlen + len - 2)); + + return data; + } + } +} From 246bba6c75a95f42b4a25464658dc04f7f76d498 Mon Sep 17 00:00:00 2001 From: Konst Kolesnichenko Date: Mon, 30 Jan 2023 00:05:35 +0100 Subject: [PATCH 2/2] * refactored Get Formatted TOC to a more reusable code * added GetBytes() --- .../MMC/GetConfigurationCommand.cs | 16 ++- .../MMC/ReadTocCommand.cs | 17 ++- .../SCSIReturnParameters/MMC/FormattedToc.cs | 104 ++++++++++++++++++ .../MMC/GetConfigurationParameter.cs | 6 +- .../SCSIReturnParameters/MMC/MmcHelper.cs | 17 +++ .../MMC/ReadTocParameter.cs | 87 ++------------- 6 files changed, 164 insertions(+), 83 deletions(-) create mode 100644 ISCSI/SCSI/SCSIReturnParameters/MMC/FormattedToc.cs create mode 100644 ISCSI/SCSI/SCSIReturnParameters/MMC/MmcHelper.cs diff --git a/ISCSI/SCSI/SCSICommandDescriptorBlock/MMC/GetConfigurationCommand.cs b/ISCSI/SCSI/SCSICommandDescriptorBlock/MMC/GetConfigurationCommand.cs index 0fd4ffc..ab45772 100644 --- a/ISCSI/SCSI/SCSICommandDescriptorBlock/MMC/GetConfigurationCommand.cs +++ b/ISCSI/SCSI/SCSICommandDescriptorBlock/MMC/GetConfigurationCommand.cs @@ -8,8 +8,7 @@ internal class GetConfigurationCommand : SCSICommandDescriptorBlock public const int PacketLength = 12; // Request Type public byte RT; - // Starting Feature Number - public ushort SFN; + public ushort StartingFeatureNumber; public short AllocationLength; public GetConfigurationCommand() @@ -21,11 +20,20 @@ public GetConfigurationCommand(byte[] buffer, int offset) { OpCode = (SCSIOpCodeName)buffer[offset + 0]; RT = (byte)(buffer[offset +1] & 0x03); - SFN = BigEndianConverter.ToUInt16(buffer, offset + 2); + StartingFeatureNumber = BigEndianConverter.ToUInt16(buffer, offset + 2); AllocationLength = BigEndianConverter.ToInt16(buffer, offset +7); TransferLength = (uint)AllocationLength; } - public override byte[] GetBytes() => throw new NotImplementedException(); + public override byte[] GetBytes() + { + byte[] buffer = new byte[PacketLength]; + buffer[0] = (byte)OpCode; + buffer[1] = RT; + BigEndianWriter.WriteUInt16(buffer, 2, StartingFeatureNumber); + BigEndianWriter.WriteInt16(buffer, 7, AllocationLength); + + return buffer; + } } } diff --git a/ISCSI/SCSI/SCSICommandDescriptorBlock/MMC/ReadTocCommand.cs b/ISCSI/SCSI/SCSICommandDescriptorBlock/MMC/ReadTocCommand.cs index fb62197..86c4a4c 100644 --- a/ISCSI/SCSI/SCSICommandDescriptorBlock/MMC/ReadTocCommand.cs +++ b/ISCSI/SCSI/SCSICommandDescriptorBlock/MMC/ReadTocCommand.cs @@ -24,8 +24,23 @@ public ReadTocCommand(byte[] buffer, int offset) TrackSessionNumber = buffer[offset + 6]; AllocationLength = BigEndianConverter.ToInt16(buffer, offset + 7); TransferLength = (uint)AllocationLength; + Control = buffer[offset + 9]; } - public override byte[] GetBytes() => throw new NotImplementedException(); + public override byte[] GetBytes() + { + byte[] buffer = new byte[PacketLength]; + buffer[0] = (byte)OpCode; + if (MSF) + { + buffer[1] |= 0x02; + } + buffer[2] |= (byte)(Format & 0xF); + buffer[6] = TrackSessionNumber; + BigEndianWriter.WriteInt16(buffer, 7, AllocationLength); + buffer[9] = Control; + + return buffer; + } } } diff --git a/ISCSI/SCSI/SCSIReturnParameters/MMC/FormattedToc.cs b/ISCSI/SCSI/SCSIReturnParameters/MMC/FormattedToc.cs new file mode 100644 index 0000000..333d58b --- /dev/null +++ b/ISCSI/SCSI/SCSIReturnParameters/MMC/FormattedToc.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SCSI +{ + internal class FormattedToc + { + const int Length = 64; + byte[] data = new byte[Length]; + bool _msf = false; + + /// + /// The TOC data length indicates the length in bytes of the following TOC data. The TOC data length value + /// does not include the TOC data length field itself.This value is not modified when the allocation length is + /// insufficient to return all of the TOC data available. + /// + private ushort TocDataLength + { + get { return BigEndianConverter.ToUInt16(data, 0); } + set { BigEndianWriter.WriteUInt16(data, 0, value); } + } + + /// + /// The First Track Number field indicates the first track number in the first complete session Table of Contents. + /// + public byte FirstTrackNumber + { + get { return data[2]; } + set { data[2] = value; } + } + + /// + /// The Last Track Number field indicates the last track number in the last complete session Table of Contents + /// before the lead-out. + /// + public byte LastTrackNumber + { + get { return data[3]; } + set { data[3] = value; } + } + + /// + /// An MSF bit of zero indicates that the Logical Block Address field + /// contains a logical block address.An MSF bit of one indicates the Logical Block Address field contains an + /// MSF address. + /// + /// + public FormattedToc(bool msf) + { + _msf = msf; + } + + /// + /// Sets TOC Track Descriptor in the Formatted TOC data array. + /// + /// Descriptor position in the data array + /// The ADR field gives the type of information encoded in the Q sub-channel of the block where this TOC + /// entry was found. + /// The Control Field indicates the attributes, of the track. + /// The Track Number field indicates the track number for which the data in the TOC track descriptor is valid. + /// A track number of AAh indicates that the track descriptor is for the start of the lead-out area. + /// The Logical Block Address contains the address of the first block with user information for that track + /// number as read from the Table of Contents. + public void SetTocTrackDescriptor(byte position, byte adr, byte control, byte trackNumber, uint address) + { + int offset = 4 + position * 8; // 4 descriptor start offset, 8 - size of descriptor (TOC/PMA/ATIP Response Data Format 0000) + // Reserved + data[offset + 0] = 0; + // ADR(7-4) CONTROL(3-0) + data[offset + 1] = (byte)(adr << 4 | (control & 0xF)); + data[offset + 2] = trackNumber; + // Reserved + data[offset + 3] = 0; + + /* Track Start Address */ + if (_msf) + { + BigEndianWriter.WriteUInt32(data, offset + 4, MmcHelper.Lba2Msf(address)); + } + else + { + BigEndianWriter.WriteUInt32(data, offset + 4, address); + } + } + + /// + /// Adjusts data length for requested track count. + /// + /// + /// + public void UpdateDataLength(int trackCount) + { + // 4 bytes of data + trackCount descriptors - TOC data length field itself + TocDataLength = (byte)(4 + trackCount * 8 - 2); + } + + public byte[] GetBytes() + { + return data; + } + } +} diff --git a/ISCSI/SCSI/SCSIReturnParameters/MMC/GetConfigurationParameter.cs b/ISCSI/SCSI/SCSIReturnParameters/MMC/GetConfigurationParameter.cs index ff1c433..5cc9dcd 100644 --- a/ISCSI/SCSI/SCSIReturnParameters/MMC/GetConfigurationParameter.cs +++ b/ISCSI/SCSI/SCSIReturnParameters/MMC/GetConfigurationParameter.cs @@ -41,7 +41,7 @@ internal byte[] GetBytes() { case 0x00: /* all of features */ - for (fc = _cmd.SFN; fc < 0xffff; fc++) + for (fc = _cmd.StartingFeatureNumber; fc < 0xffff; fc++) { plen = istgt_lu_dvd_get_feature_descriptor(fc, data, hlen + len); len += plen; @@ -50,7 +50,7 @@ internal byte[] GetBytes() case 0x01: /* current of features */ - for (fc = _cmd.SFN; fc < 0xffff; fc++) + for (fc = _cmd.StartingFeatureNumber; fc < 0xffff; fc++) { plen = istgt_lu_dvd_get_feature_descriptor(fc, data, hlen + len); if (data[hlen + 2] == 1) @@ -66,7 +66,7 @@ internal byte[] GetBytes() case 0x02: /* specified feature */ - fc = _cmd.SFN; + fc = _cmd.StartingFeatureNumber; plen = istgt_lu_dvd_get_feature_descriptor(fc, data, hlen + len); len += plen; break; diff --git a/ISCSI/SCSI/SCSIReturnParameters/MMC/MmcHelper.cs b/ISCSI/SCSI/SCSIReturnParameters/MMC/MmcHelper.cs new file mode 100644 index 0000000..5ee9d58 --- /dev/null +++ b/ISCSI/SCSI/SCSIReturnParameters/MMC/MmcHelper.cs @@ -0,0 +1,17 @@ +namespace SCSI +{ + public static class MmcHelper + { + public static uint Lba2Msf(uint lba) + { + uint m, s, f; + + lba += 150; + m = (lba / 75) / 60; + s = (lba / 75) % 60; + f = lba % 75; + + return ((m << 16) | (s << 8) | f); + } + } +} diff --git a/ISCSI/SCSI/SCSIReturnParameters/MMC/ReadTocParameter.cs b/ISCSI/SCSI/SCSIReturnParameters/MMC/ReadTocParameter.cs index 9614316..7c8ac19 100644 --- a/ISCSI/SCSI/SCSIReturnParameters/MMC/ReadTocParameter.cs +++ b/ISCSI/SCSI/SCSIReturnParameters/MMC/ReadTocParameter.cs @@ -20,7 +20,18 @@ internal byte[] GetBytes() { switch(_format) { - case 0: return GetFormattedTOC(); + case 0: + { + FormattedToc toc = new FormattedToc(_msf); + toc.FirstTrackNumber = 1; + toc.LastTrackNumber = 1; + toc.SetTocTrackDescriptor(0, 1, 4, 1, 0); + // Lead-Out + toc.SetTocTrackDescriptor(1, 1, 4, 0xAA, (uint)_disk.TotalSectors); + toc.UpdateDataLength(2); + + return toc.GetBytes(); + } case 1: return MultiSessionInformation(); case 2: return RawTOC(); default: return null; @@ -223,79 +234,5 @@ private byte[] MultiSessionInformation() return data; } - - internal byte[] GetFormattedTOC() - { - sbyte hlen; - sbyte len = 0, plen; - - byte[] data = new byte[Length]; - - /* First Track Number */ - data[2] = 1; - /* Last Track Number */ - data[3] = 1; - hlen = 4; - - /* TOC Track Descriptor */ - /* Track 1 Descriptor */ - int offset = hlen + len; - - /* Reserved */ - data[offset + 0] = 0; - /* ADR(7-4) CONTROL(3-0) */ - data[offset + 1] = 0x14; - /* Track Number */ - data[offset + 2] = 1; - /* Reserved */ - data[offset + 3] = 0; - - /* Track Start Address */ - if (_msf) - { - // DSET32(&cp[4], istgt_lba2msf(0)); - BigEndianWriter.WriteInt32(data, offset + 4, /*lba2msf*/ 0); - } - else - { - BigEndianWriter.WriteInt32(data, offset + 4, 0); - } - - plen = 8; - len += plen; - - /* Track AAh (Lead-out) Descriptor */ - // cp = &data[hlen + len]; - offset = hlen + len; - - /* Reserved */ - data[offset + 0] = 0; - /* ADR(7-4) CONTROL(3-0) */ - data[offset + 1] = 0x14; - /* Track Number */ - data[offset + 2] = 0xaa; - /* Reserved */ - data[offset + 3] = 0; - /* Track Start Address */ - if (_msf) - { - // DSET32(&cp[4], istgt_lba2msf(spec->blockcnt)); - BigEndianWriter.WriteInt32(data, offset + 4, /*lba2msf*/ (int)_disk.TotalSectors); - } - else - { - // DSET32(&cp[4], spec->blockcnt); - BigEndianWriter.WriteInt32(data, offset + 4, (int)_disk.TotalSectors); - } - - plen = 8; - len += plen; - - /* TOC Data Length */ - // DSET16(&data[0], hlen + len - 2) - BigEndianWriter.WriteInt16(data, 0, (short)(hlen + len - 2)); - - return data; - } } }