Пример #1
0
        public Scsi ReportScsiInquiry()
        {
            DicConsole.WriteLine("Querying SCSI INQUIRY...");
            bool sense = _dev.ScsiInquiry(out byte[] buffer, out byte[] senseBuffer);

            var report = new Scsi();

            if (sense)
            {
                return(null);
            }

            Inquiry?decodedNullable = Inquiry.Decode(buffer);

            if (!decodedNullable.HasValue)
            {
                return(null);
            }

            Inquiry decoded = decodedNullable.Value;

            // Clear Seagate serial number
            if (decoded.SeagatePresent &&
                StringHandlers.CToString(decoded.VendorIdentification)?.Trim().ToLowerInvariant() == "seagate")
            {
                for (int i = 36; i <= 43; i++)
                {
                    buffer[i] = 0;
                }
            }

            report.InquiryData = buffer;

            return(report);
        }
Пример #2
0
        public DeviceInfo(Device dev)
        {
            Type                  = dev.Type;
            Manufacturer          = dev.Manufacturer;
            Model                 = dev.Model;
            FirmwareRevision      = dev.FirmwareRevision;
            Serial                = dev.Serial;
            ScsiType              = dev.ScsiType;
            IsRemovable           = dev.IsRemovable;
            IsUsb                 = dev.IsUsb;
            UsbVendorId           = dev.UsbVendorId;
            UsbProductId          = dev.UsbProductId;
            UsbDescriptors        = dev.UsbDescriptors;
            UsbManufacturerString = dev.UsbManufacturerString;
            UsbProductString      = dev.UsbProductString;
            UsbSerialString       = dev.UsbSerialString;
            IsFireWire            = dev.IsFireWire;
            FireWireGuid          = dev.FireWireGuid;
            FireWireModel         = dev.FireWireModel;
            FireWireModelName     = dev.FireWireModelName;
            FireWireVendor        = dev.FireWireVendor;
            FireWireVendorName    = dev.FireWireVendorName;
            IsCompactFlash        = dev.IsCompactFlash;
            IsPcmcia              = dev.IsPcmcia;
            Cis = dev.Cis;

            switch (dev.Type)
            {
            case DeviceType.ATA:
            {
                bool sense = dev.AtaIdentify(out byte[] ataBuf, out AtaErrorRegistersChs errorRegisters);

                if (sense)
                {
                    DicConsole.DebugWriteLine("Device-Info command", "STATUS = 0x{0:X2}", errorRegisters.Status);
                    DicConsole.DebugWriteLine("Device-Info command", "ERROR = 0x{0:X2}", errorRegisters.Error);

                    DicConsole.DebugWriteLine("Device-Info command", "NSECTOR = 0x{0:X2}",
                                              errorRegisters.SectorCount);

                    DicConsole.DebugWriteLine("Device-Info command", "SECTOR = 0x{0:X2}", errorRegisters.Sector);

                    DicConsole.DebugWriteLine("Device-Info command", "CYLHIGH = 0x{0:X2}",
                                              errorRegisters.CylinderHigh);

                    DicConsole.DebugWriteLine("Device-Info command", "CYLLOW = 0x{0:X2}",
                                              errorRegisters.CylinderLow);

                    DicConsole.DebugWriteLine("Device-Info command", "DEVICE = 0x{0:X2}",
                                              errorRegisters.DeviceHead);

                    DicConsole.DebugWriteLine("Device-Info command", "Error code = {0}", dev.LastError);

                    break;
                }

                if (dev.Error)
                {
                    DicConsole.ErrorWriteLine("Error {0} querying ATA IDENTIFY", dev.LastError);

                    break;
                }

                AtaIdentify = ataBuf;

                dev.EnableMediaCardPassThrough(out errorRegisters, dev.Timeout, out _);

                if (errorRegisters.Sector == 0xAA &&
                    errorRegisters.SectorCount == 0x55)
                {
                    AtaMcptError = errorRegisters;
                }

                break;
            }

            case DeviceType.ATAPI:
            {
                bool sense = dev.AtapiIdentify(out byte[] ataBuf, out AtaErrorRegistersChs errorRegisters);

                if (sense)
                {
                    DicConsole.DebugWriteLine("Device-Info command", "STATUS = 0x{0:X2}", errorRegisters.Status);
                    DicConsole.DebugWriteLine("Device-Info command", "ERROR = 0x{0:X2}", errorRegisters.Error);

                    DicConsole.DebugWriteLine("Device-Info command", "NSECTOR = 0x{0:X2}",
                                              errorRegisters.SectorCount);

                    DicConsole.DebugWriteLine("Device-Info command", "SECTOR = 0x{0:X2}", errorRegisters.Sector);

                    DicConsole.DebugWriteLine("Device-Info command", "CYLHIGH = 0x{0:X2}",
                                              errorRegisters.CylinderHigh);

                    DicConsole.DebugWriteLine("Device-Info command", "CYLLOW = 0x{0:X2}",
                                              errorRegisters.CylinderLow);

                    DicConsole.DebugWriteLine("Device-Info command", "DEVICE = 0x{0:X2}",
                                              errorRegisters.DeviceHead);

                    DicConsole.DebugWriteLine("Device-Info command", "Error code = {0}", dev.LastError);

                    break;
                }

                if (!dev.Error)
                {
                    AtapiIdentify = ataBuf;
                }
                else
                {
                    DicConsole.ErrorWriteLine("Error {0} querying ATA PACKET IDENTIFY", dev.LastError);
                }

                // ATAPI devices are also SCSI devices
                goto case DeviceType.SCSI;
            }

            case DeviceType.SCSI:
            {
                bool sense = dev.ScsiInquiry(out byte[] inqBuf, out byte[] senseBuf);

                if (sense)
                {
                    DicConsole.ErrorWriteLine("SCSI error:\n{0}", Sense.PrettifySense(senseBuf));

                    break;
                }

                ScsiInquiryData = inqBuf;
                ScsiInquiry     = Inquiry.Decode(inqBuf);

                sense = dev.ScsiInquiry(out inqBuf, out senseBuf, 0x00);

                if (!sense)
                {
                    ScsiEvpdPages = new Dictionary <byte, byte[]>();

                    byte[] pages = EVPD.DecodePage00(inqBuf);

                    if (pages != null)
                    {
                        foreach (byte page in pages)
                        {
                            sense = dev.ScsiInquiry(out inqBuf, out senseBuf, page);

                            if (sense)
                            {
                                continue;
                            }

                            ScsiEvpdPages.Add(page, inqBuf);
                        }
                    }
                }

                var devType = (PeripheralDeviceTypes)ScsiInquiry.Value.PeripheralDeviceType;

                sense = dev.ModeSense10(out byte[] modeBuf, out senseBuf, false, true,
                                        ScsiModeSensePageControl.Current, 0x3F, 0xFF, 5, out _);

                if (!sense &&
                    !dev.Error)
                {
                    ScsiModeSense10 = modeBuf;
                }

                if (sense || dev.Error)
                {
                    sense = dev.ModeSense10(out modeBuf, out senseBuf, false, true,
                                            ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out _);

                    if (!sense &&
                        !dev.Error)
                    {
                        ScsiModeSense10 = modeBuf;
                    }
                }

                if (!sense &&
                    !dev.Error)
                {
                    ScsiMode = Modes.DecodeMode10(modeBuf, devType);
                }

                bool useMode10 = !(sense || dev.Error || !ScsiMode.HasValue);

                sense = dev.ModeSense6(out modeBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F,
                                       0xFF, 5, out _);

                if (!sense &&
                    !dev.Error)
                {
                    ScsiModeSense6 = modeBuf;
                }

                if (sense || dev.Error)
                {
                    sense = dev.ModeSense6(out modeBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F,
                                           0x00, 5, out _);

                    if (!sense &&
                        !dev.Error)
                    {
                        ScsiModeSense6 = modeBuf;
                    }
                }

                if (sense || dev.Error)
                {
                    sense = dev.ModeSense(out modeBuf, out senseBuf, 5, out _);

                    if (!sense &&
                        !dev.Error)
                    {
                        ScsiModeSense6 = modeBuf;
                    }
                }

                if (!sense &&
                    !dev.Error &&
                    !useMode10)
                {
                    ScsiMode = Modes.DecodeMode6(modeBuf, devType);
                }

                switch (devType)
                {
                case PeripheralDeviceTypes.MultiMediaDevice:
                {
                    sense = dev.GetConfiguration(out byte[] confBuf, out senseBuf, dev.Timeout, out _);

                    if (!sense)
                    {
                        MmcConfiguration = confBuf;
                    }

                    // TODO: DVD drives respond correctly to BD status.
                    // While specification says if no medium is present
                    // it should inform all possible capabilities,
                    // testing drives show only supported media capabilities.

                    /*
                     * byte[] strBuf;
                     * sense = dev.ReadDiscStructure(out strBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.CapabilityList, 0, dev.Timeout, out _);
                     *
                     * if (!sense)
                     * {
                     *  Decoders.SCSI.DiscStructureCapabilities.Capability[] caps = Decoders.SCSI.DiscStructureCapabilities.Decode(strBuf);
                     *  if (caps != null)
                     *  {
                     *      foreach (Decoders.SCSI.DiscStructureCapabilities.Capability cap in caps)
                     *      {
                     *          if (cap.SDS && cap.RDS)
                     *              DicConsole.WriteLine("Drive can READ/SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *          else if (cap.SDS)
                     *              DicConsole.WriteLine("Drive can SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *          else if (cap.RDS)
                     *              DicConsole.WriteLine("Drive can READ DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *      }
                     *  }
                     * }
                     *
                     * sense = dev.ReadDiscStructure(out strBuf, out senseBuf, MmcDiscStructureMediaType.BD, 0, 0, MmcDiscStructureFormat.CapabilityList, 0, dev.Timeout, out _);
                     *
                     * if (!sense)
                     * {
                     *  Decoders.SCSI.DiscStructureCapabilities.Capability[] caps = Decoders.SCSI.DiscStructureCapabilities.Decode(strBuf);
                     *  if (caps != null)
                     *  {
                     *      foreach (Decoders.SCSI.DiscStructureCapabilities.Capability cap in caps)
                     *      {
                     *          if (cap.SDS && cap.RDS)
                     *              DicConsole.WriteLine("Drive can READ/SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *          else if (cap.SDS)
                     *              DicConsole.WriteLine("Drive can SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *          else if (cap.RDS)
                     *              DicConsole.WriteLine("Drive can READ DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *      }
                     *  }
                     * }
                     */

                    #region Plextor
                    if (dev.Manufacturer == "PLEXTOR")
                    {
                        bool   plxtSense = true;
                        bool   plxtDvd   = false;
                        byte[] plxtBuf   = null;

                        switch (dev.Model)
                        {
                        case "DVDR   PX-708A":
                        case "DVDR   PX-708A2":
                        case "DVDR   PX-712A":
                            plxtDvd = true;

                            plxtSense = dev.PlextorReadEeprom(out plxtBuf, out senseBuf, dev.Timeout,
                                                              out _);

                            break;

                        case "DVDR   PX-714A":
                        case "DVDR   PX-716A":
                        case "DVDR   PX-716AL":
                        case "DVDR   PX-755A":
                        case "DVDR   PX-760A":
                        {
                            plxtBuf = new byte[256 * 4];

                            for (byte i = 0; i < 4; i++)
                            {
                                plxtSense = dev.PlextorReadEepromBlock(out byte[] plxtBufSmall,
                                                                       out senseBuf, i, 256, dev.Timeout,
                                                                       out _);

                                if (plxtSense)
                                {
                                    break;
                                }

                                Array.Copy(plxtBufSmall, 0, plxtBuf, i * 256, 256);
                            }

                            plxtDvd = true;

                            break;
                        }

                        default:
                        {
                            if (dev.Model.StartsWith("CD-R   ", StringComparison.Ordinal))
                            {
                                plxtSense = dev.PlextorReadEepromCdr(out plxtBuf, out senseBuf, dev.Timeout,
                                                                     out _);
                            }

                            break;
                        }
                        }

                        PlextorFeatures = new Plextor
                        {
                            IsDvd = plxtDvd
                        };

                        if (!plxtSense)
                        {
                            PlextorFeatures.Eeprom = plxtBuf;

                            if (plxtDvd)
                            {
                                PlextorFeatures.Discs        = BigEndianBitConverter.ToUInt16(plxtBuf, 0x0120);
                                PlextorFeatures.CdReadTime   = BigEndianBitConverter.ToUInt32(plxtBuf, 0x0122);
                                PlextorFeatures.CdWriteTime  = BigEndianBitConverter.ToUInt32(plxtBuf, 0x0126);
                                PlextorFeatures.DvdReadTime  = BigEndianBitConverter.ToUInt32(plxtBuf, 0x012A);
                                PlextorFeatures.DvdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x012E);
                            }
                            else
                            {
                                PlextorFeatures.Discs       = BigEndianBitConverter.ToUInt16(plxtBuf, 0x0078);
                                PlextorFeatures.CdReadTime  = BigEndianBitConverter.ToUInt32(plxtBuf, 0x006C);
                                PlextorFeatures.CdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x007A);
                            }
                        }

                        plxtSense = dev.PlextorGetPoweRec(out senseBuf, out bool plxtPwrRecEnabled,
                                                          out ushort plxtPwrRecSpeed, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.PoweRec = true;

                            if (plxtPwrRecEnabled)
                            {
                                PlextorFeatures.PoweRecEnabled          = true;
                                PlextorFeatures.PoweRecRecommendedSpeed = plxtPwrRecSpeed;

                                plxtSense = dev.PlextorGetSpeeds(out senseBuf, out ushort plxtPwrRecSelected,
                                                                 out ushort plxtPwrRecMax,
                                                                 out ushort plxtPwrRecLast, dev.Timeout, out _);

                                if (!plxtSense)
                                {
                                    PlextorFeatures.PoweRecSelected = plxtPwrRecSelected;
                                    PlextorFeatures.PoweRecMax      = plxtPwrRecMax;
                                    PlextorFeatures.PoweRecLast     = plxtPwrRecLast;
                                }
                            }
                        }

                        // TODO: Check it with a drive
                        plxtSense = dev.PlextorGetSilentMode(out plxtBuf, out senseBuf, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            if (plxtBuf[0] == 1)
                            {
                                PlextorFeatures.SilentModeEnabled = true;
                                PlextorFeatures.AccessTimeLimit   = plxtBuf[1];

                                PlextorFeatures.CdReadSpeedLimit  = plxtBuf[2];
                                PlextorFeatures.DvdReadSpeedLimit = plxtBuf[3];
                                PlextorFeatures.CdWriteSpeedLimit = plxtBuf[4];

                                // TODO: Check which one is each one

                                /*
                                 *  if(plxtBuf[6] > 0)
                                 *      DicConsole.WriteLine("\tTray eject speed limited to {0}",
                                 *                           -(plxtBuf[6] + 48));
                                 *  if(plxtBuf[7] > 0)
                                 *      DicConsole.WriteLine("\tTray eject speed limited to {0}",
                                 *                           plxtBuf[7] - 47);
                                 */
                            }
                        }

                        plxtSense = dev.PlextorGetGigaRec(out plxtBuf, out senseBuf, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.GigaRec = true;
                        }

                        plxtSense = dev.PlextorGetSecuRec(out plxtBuf, out senseBuf, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.SecuRec = true;
                        }

                        plxtSense = dev.PlextorGetSpeedRead(out plxtBuf, out senseBuf, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.SpeedRead = true;

                            if ((plxtBuf[2] & 0x01) == 0x01)
                            {
                                PlextorFeatures.SpeedReadEnabled = true;
                            }
                        }

                        plxtSense = dev.PlextorGetHiding(out plxtBuf, out senseBuf, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.Hiding = true;

                            if ((plxtBuf[2] & 0x02) == 0x02)
                            {
                                PlextorFeatures.HidesRecordables = true;
                            }

                            if ((plxtBuf[2] & 0x01) == 0x01)
                            {
                                PlextorFeatures.HidesSessions = true;
                            }
                        }

                        plxtSense = dev.PlextorGetVariRec(out plxtBuf, out senseBuf, false, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.VariRec = true;
                        }

                        if (plxtDvd)
                        {
                            plxtSense = dev.PlextorGetVariRec(out plxtBuf, out senseBuf, true, dev.Timeout,
                                                              out _);

                            if (!plxtSense)
                            {
                                PlextorFeatures.VariRecDvd = true;
                            }

                            plxtSense = dev.PlextorGetBitsetting(out plxtBuf, out senseBuf, false, dev.Timeout,
                                                                 out _);

                            if (!plxtSense)
                            {
                                PlextorFeatures.BitSetting = true;
                            }

                            plxtSense = dev.PlextorGetBitsetting(out plxtBuf, out senseBuf, true, dev.Timeout,
                                                                 out _);

                            if (!plxtSense)
                            {
                                PlextorFeatures.BitSettingDl = true;
                            }

                            plxtSense = dev.PlextorGetTestWriteDvdPlus(out plxtBuf, out senseBuf, dev.Timeout,
                                                                       out _);

                            if (!plxtSense)
                            {
                                PlextorFeatures.DvdPlusWriteTest = true;
                            }
                        }
                    }
                    #endregion Plextor

                    if (ScsiInquiry.Value.KreonPresent)
                    {
                        if (!dev.KreonGetFeatureList(out senseBuf, out KreonFeatures krFeatures, dev.Timeout,
                                                     out _))
                        {
                            KreonFeatures = krFeatures;
                        }
                    }

                    break;
                }

                case PeripheralDeviceTypes.SequentialAccess:
                {
                    sense = dev.ReadBlockLimits(out byte[] seqBuf, out senseBuf, dev.Timeout, out _);

                    if (sense)
                    {
                        DicConsole.ErrorWriteLine("READ BLOCK LIMITS:\n{0}", Sense.PrettifySense(senseBuf));
                    }
                    else
                    {
                        BlockLimits = seqBuf;
                    }

                    sense = dev.ReportDensitySupport(out seqBuf, out senseBuf, dev.Timeout, out _);

                    if (sense)
                    {
                        DicConsole.ErrorWriteLine("REPORT DENSITY SUPPORT:\n{0}",
                                                  Sense.PrettifySense(senseBuf));
                    }
                    else
                    {
                        DensitySupport       = seqBuf;
                        DensitySupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeDensity(seqBuf);
                    }

                    sense = dev.ReportDensitySupport(out seqBuf, out senseBuf, true, false, dev.Timeout, out _);

                    if (sense)
                    {
                        DicConsole.ErrorWriteLine("REPORT DENSITY SUPPORT (MEDIUM):\n{0}",
                                                  Sense.PrettifySense(senseBuf));
                    }
                    else
                    {
                        MediumDensitySupport   = seqBuf;
                        MediaTypeSupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeMediumType(seqBuf);
                    }

                    break;
                }
                }

                break;
            }

            case DeviceType.MMC:
            {
                bool sense = dev.ReadCid(out byte[] mmcBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    CID = mmcBuf;
                }

                sense = dev.ReadCsd(out mmcBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    CSD = mmcBuf;
                }

                sense = dev.ReadOcr(out mmcBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    OCR = mmcBuf;
                }

                sense = dev.ReadExtendedCsd(out mmcBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    ExtendedCSD = mmcBuf;
                }
            }

            break;

            case DeviceType.SecureDigital:
            {
                bool sense = dev.ReadCid(out byte[] sdBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    CID = sdBuf;
                }

                sense = dev.ReadCsd(out sdBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    CSD = sdBuf;
                }

                sense = dev.ReadSdocr(out sdBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    OCR = sdBuf;
                }

                sense = dev.ReadScr(out sdBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    SCR = sdBuf;
                }
            }

            break;

            default:
                DicConsole.ErrorWriteLine("Unknown device type {0}, cannot get information.", dev.Type);

                break;
            }
        }
        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.IsHiMD)
            {
                length = 56;
                Array.Copy(HiMDSignatureContents, 0, buffer, 36, 8);

                if (decoded.HiMDSpecific != null)
                {
                    Array.Copy(decoded.HiMDSpecific, 0, buffer, 44, 12);
                }
            }

            if (decoded.VendorSpecific != null &&
                !decoded.IsHiMD)
            {
                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);
        }
        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);

                // Hi-MD
                decoded.HiMDSignature = new byte[8];
                Array.Copy(SCSIInquiryResponse, 36, decoded.HiMDSignature, 0, 8);
                decoded.IsHiMD = HiMDSignatureContents.SequenceEqual(decoded.HiMDSignature);
            }

            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)
            {
                if (decoded.IsHiMD)
                {
                    decoded.HiMDSpecific = new byte[12];
                    Array.Copy(SCSIInquiryResponse, 44, decoded.HiMDSpecific, 0, 12);
                }
                else
                {
                    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);
        }