public DeviceInfo(Device dev)
        {
            Type                  = dev.Type;
            Manufacturer          = dev.Manufacturer;
            Model                 = dev.Model;
            Revision              = dev.Revision;
            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);
                        }
                    }
                }

                PeripheralDeviceTypes 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;
            }
        }
        /// <summary>
        ///     Opens the device for sending direct commands
        /// </summary>
        /// <param name="devicePath">Device path</param>
        public Device(string devicePath)
        {
            PlatformId  = DetectOS.GetRealPlatformID();
            Timeout     = 15;
            Error       = false;
            IsRemovable = false;

            switch (PlatformId)
            {
            case PlatformID.Win32NT:
            {
                FileHandle = Extern.CreateFile(devicePath, FileAccess.GenericRead | FileAccess.GenericWrite,
                                               FileShare.Read | FileShare.Write, IntPtr.Zero,
                                               FileMode.OpenExisting,
                                               FileAttributes.Normal, IntPtr.Zero);

                if (((SafeFileHandle)FileHandle).IsInvalid)
                {
                    Error     = true;
                    LastError = Marshal.GetLastWin32Error();
                }

                break;
            }

            case PlatformID.Linux:
            {
                FileHandle =
                    Linux.Extern.open(devicePath,
                                      FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew);

                if ((int)FileHandle < 0)
                {
                    LastError = Marshal.GetLastWin32Error();

                    if (LastError == 13 || LastError == 30)    // EACCES or EROFS
                    {
                        FileHandle = Linux.Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking);
                        if ((int)FileHandle < 0)
                        {
                            Error     = true;
                            LastError = Marshal.GetLastWin32Error();
                        }
                    }
                    else
                    {
                        Error = true;
                    }

                    LastError = Marshal.GetLastWin32Error();
                }

                break;
            }

            case PlatformID.FreeBSD:
            {
                FileHandle = FreeBSD.Extern.cam_open_device(devicePath, FreeBSD.FileFlags.ReadWrite);

                if (((IntPtr)FileHandle).ToInt64() == 0)
                {
                    Error     = true;
                    LastError = Marshal.GetLastWin32Error();
                }

                CamDevice camDevice = (CamDevice)Marshal.PtrToStructure((IntPtr)FileHandle, typeof(CamDevice));

                if (StringHandlers.CToString(camDevice.SimName) == "ata")
                {
                    throw new
                          InvalidOperationException("Parallel ATA devices are not supported on FreeBSD due to upstream bug #224250.");
                }

                break;
            }

            default: throw new InvalidOperationException($"Platform {PlatformId} not yet supported.");
            }

            if (Error)
            {
                throw new SystemException($"Error {LastError} opening device.");
            }

            Type     = DeviceType.Unknown;
            ScsiType = PeripheralDeviceTypes.UnknownDevice;

            byte[] ataBuf;
            byte[] inqBuf = null;

            if (Error)
            {
                throw new SystemException($"Error {LastError} trying device.");
            }

            bool scsiSense = true;

            // Windows is answering SCSI INQUIRY for all device types so it needs to be detected first
            switch (PlatformId)
            {
            case PlatformID.Win32NT:
                StoragePropertyQuery query = new StoragePropertyQuery();
                query.PropertyId           = StoragePropertyId.Device;
                query.QueryType            = StorageQueryType.Standard;
                query.AdditionalParameters = new byte[1];

                IntPtr descriptorPtr = Marshal.AllocHGlobal(1000);
                byte[] descriptorB   = new byte[1000];

                uint returned = 0;
                int  error    = 0;

                bool hasError = !Extern.DeviceIoControlStorageQuery((SafeFileHandle)FileHandle,
                                                                    WindowsIoctl.IoctlStorageQueryProperty,
                                                                    ref query, (uint)Marshal.SizeOf(query),
                                                                    descriptorPtr, 1000, ref returned, IntPtr.Zero);

                if (hasError)
                {
                    error = Marshal.GetLastWin32Error();
                }

                Marshal.Copy(descriptorPtr, descriptorB, 0, 1000);

                if (!hasError && error == 0)
                {
                    StorageDeviceDescriptor descriptor = new StorageDeviceDescriptor
                    {
                        Version               = BitConverter.ToUInt32(descriptorB, 0),
                        Size                  = BitConverter.ToUInt32(descriptorB, 4),
                        DeviceType            = descriptorB[8],
                        DeviceTypeModifier    = descriptorB[9],
                        RemovableMedia        = descriptorB[10] > 0,
                        CommandQueueing       = descriptorB[11] > 0,
                        VendorIdOffset        = BitConverter.ToInt32(descriptorB, 12),
                        ProductIdOffset       = BitConverter.ToInt32(descriptorB, 16),
                        ProductRevisionOffset = BitConverter.ToInt32(descriptorB, 20),
                        SerialNumberOffset    = BitConverter.ToInt32(descriptorB, 24),
                        BusType               = (StorageBusType)BitConverter.ToUInt32(descriptorB, 28),
                        RawPropertiesLength   = BitConverter.ToUInt32(descriptorB, 32)
                    };
                    descriptor.RawDeviceProperties = new byte[descriptor.RawPropertiesLength];
                    Array.Copy(descriptorB, 36, descriptor.RawDeviceProperties, 0, descriptor.RawPropertiesLength);

                    switch (descriptor.BusType)
                    {
                    case StorageBusType.SCSI:
                    case StorageBusType.SSA:
                    case StorageBusType.Fibre:
                    case StorageBusType.iSCSI:
                    case StorageBusType.SAS:
                        Type = DeviceType.SCSI;
                        break;

                    case StorageBusType.FireWire:
                        IsFireWire = true;
                        Type       = DeviceType.SCSI;
                        break;

                    case StorageBusType.USB:
                        IsUsb = true;
                        Type  = DeviceType.SCSI;
                        break;

                    case StorageBusType.ATAPI:
                        Type = DeviceType.ATAPI;
                        break;

                    case StorageBusType.ATA:
                    case StorageBusType.SATA:
                        Type = DeviceType.ATA;
                        break;

                    case StorageBusType.MultiMediaCard:
                        Type = DeviceType.MMC;
                        break;

                    case StorageBusType.SecureDigital:
                        Type = DeviceType.SecureDigital;
                        break;

                    case StorageBusType.NVMe:
                        Type = DeviceType.NVMe;
                        break;
                    }

                    switch (Type)
                    {
                    case DeviceType.SCSI:
                    case DeviceType.ATAPI:
                        scsiSense = ScsiInquiry(out inqBuf, out _);
                        break;

                    case DeviceType.ATA:
                        bool atapiSense = AtapiIdentify(out ataBuf, out _);

                        if (!atapiSense)
                        {
                            Type = DeviceType.ATAPI;
                            Identify.IdentifyDevice?ataid = Identify.Decode(ataBuf);

                            if (ataid.HasValue)
                            {
                                scsiSense = ScsiInquiry(out inqBuf, out _);
                            }
                        }
                        else
                        {
                            Manufacturer = "ATA";
                        }

                        break;
                    }
                }

                Marshal.FreeHGlobal(descriptorPtr);

                if (Windows.Command.IsSdhci((SafeFileHandle)FileHandle))
                {
                    byte[] sdBuffer = new byte[16];

                    LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendCsd,
                                                               false, false,
                                                               MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 |
                                                               MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, out _,
                                                               out _, out bool sense);

                    if (!sense)
                    {
                        cachedCsd = new byte[16];
                        Array.Copy(sdBuffer, 0, cachedCsd, 0, 16);
                    }

                    sdBuffer = new byte[16];

                    LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendCid,
                                                               false, false,
                                                               MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 |
                                                               MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, out _,
                                                               out _, out sense);

                    if (!sense)
                    {
                        cachedCid = new byte[16];
                        Array.Copy(sdBuffer, 0, cachedCid, 0, 16);
                    }

                    sdBuffer = new byte[8];

                    LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle,
                                                               (MmcCommands)SecureDigitalCommands.SendScr, false,
                                                               true,
                                                               MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 |
                                                               MmcFlags.CommandAdtc, 0, 8, 1, ref sdBuffer, out _,
                                                               out _, out sense);

                    if (!sense)
                    {
                        cachedScr = new byte[8];
                        Array.Copy(sdBuffer, 0, cachedScr, 0, 8);
                    }

                    if (cachedScr != null)
                    {
                        sdBuffer = new byte[4];

                        LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle,
                                                                   (MmcCommands)SecureDigitalCommands
                                                                   .SendOperatingCondition, false, true,
                                                                   MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 |
                                                                   MmcFlags.CommandBcr, 0, 4, 1, ref sdBuffer,
                                                                   out _, out _, out sense);

                        if (!sense)
                        {
                            cachedScr = new byte[4];
                            Array.Copy(sdBuffer, 0, cachedScr, 0, 4);
                        }
                    }
                    else
                    {
                        sdBuffer = new byte[4];

                        LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle,
                                                                   MmcCommands.SendOpCond, false, true,
                                                                   MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 |
                                                                   MmcFlags.CommandBcr, 0, 4, 1, ref sdBuffer,
                                                                   out _, out _, out sense);

                        if (!sense)
                        {
                            cachedScr = new byte[4];
                            Array.Copy(sdBuffer, 0, cachedScr, 0, 4);
                        }
                    }
                }

                break;

            case PlatformID.Linux:
                if (devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
                {
                    scsiSense = ScsiInquiry(out inqBuf, out _);
                }
                // MultiMediaCard and SecureDigital go here
                else if (devicePath.StartsWith("/dev/mmcblk", StringComparison.Ordinal))
                {
                    string devPath = devicePath.Substring(5);
                    if (File.Exists("/sys/block/" + devPath + "/device/csd"))
                    {
                        int len =
                            ConvertFromHexAscii("/sys/block/" + devPath + "/device/csd", out cachedCsd);
                        if (len == 0)
                        {
                            cachedCsd = null;
                        }
                    }

                    if (File.Exists("/sys/block/" + devPath + "/device/cid"))
                    {
                        int len =
                            ConvertFromHexAscii("/sys/block/" + devPath + "/device/cid", out cachedCid);
                        if (len == 0)
                        {
                            cachedCid = null;
                        }
                    }

                    if (File.Exists("/sys/block/" + devPath + "/device/scr"))
                    {
                        int len =
                            ConvertFromHexAscii("/sys/block/" + devPath + "/device/scr", out cachedScr);
                        if (len == 0)
                        {
                            cachedScr = null;
                        }
                    }

                    if (File.Exists("/sys/block/" + devPath + "/device/ocr"))
                    {
                        int len =
                            ConvertFromHexAscii("/sys/block/" + devPath + "/device/ocr", out cachedOcr);
                        if (len == 0)
                        {
                            cachedOcr = null;
                        }
                    }
                }

                break;

            default:
                scsiSense = ScsiInquiry(out inqBuf, out _);
                break;
            }

            #region SecureDigital / MultiMediaCard
            if (cachedCid != null)
            {
                ScsiType    = PeripheralDeviceTypes.DirectAccess;
                IsRemovable = false;

                if (cachedScr != null)
                {
                    Type = DeviceType.SecureDigital;
                    CID decoded = Decoders.SecureDigital.Decoders.DecodeCID(cachedCid);
                    Manufacturer = VendorString.Prettify(decoded.Manufacturer);
                    Model        = decoded.ProductName;
                    Revision     = $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}";
                    Serial       = $"{decoded.ProductSerialNumber}";
                }
                else
                {
                    Type = DeviceType.MMC;
                    Decoders.MMC.CID decoded = Decoders.MMC.Decoders.DecodeCID(cachedCid);
                    Manufacturer = Decoders.MMC.VendorString.Prettify(decoded.Manufacturer);
                    Model        = decoded.ProductName;
                    Revision     = $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}";
                    Serial       = $"{decoded.ProductSerialNumber}";
                }
            }
            #endregion SecureDigital / MultiMediaCard

            #region USB
            switch (PlatformId)
            {
            case PlatformID.Linux:
                if (devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
                {
                    string devPath = devicePath.Substring(5);
                    if (Directory.Exists("/sys/block/" + devPath))
                    {
                        string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath);
                        resolvedLink = "/sys" + resolvedLink.Substring(2);
                        if (!string.IsNullOrEmpty(resolvedLink))
                        {
                            while (resolvedLink.Contains("usb"))
                            {
                                resolvedLink = Path.GetDirectoryName(resolvedLink);
                                if (!File.Exists(resolvedLink + "/descriptors") ||
                                    !File.Exists(resolvedLink + "/idProduct") ||
                                    !File.Exists(resolvedLink + "/idVendor"))
                                {
                                    continue;
                                }

                                FileStream usbFs = new FileStream(resolvedLink + "/descriptors",
                                                                  System.IO.FileMode.Open,
                                                                  System.IO.FileAccess.Read);
                                byte[] usbBuf   = new byte[65536];
                                int    usbCount = usbFs.Read(usbBuf, 0, 65536);
                                UsbDescriptors = new byte[usbCount];
                                Array.Copy(usbBuf, 0, UsbDescriptors, 0, usbCount);
                                usbFs.Close();

                                StreamReader usbSr   = new StreamReader(resolvedLink + "/idProduct");
                                string       usbTemp = usbSr.ReadToEnd();
                                ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                                out usbProduct);
                                usbSr.Close();

                                usbSr   = new StreamReader(resolvedLink + "/idVendor");
                                usbTemp = usbSr.ReadToEnd();
                                ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                                out usbVendor);
                                usbSr.Close();

                                if (File.Exists(resolvedLink + "/manufacturer"))
                                {
                                    usbSr = new StreamReader(resolvedLink + "/manufacturer");
                                    UsbManufacturerString = usbSr.ReadToEnd().Trim();
                                    usbSr.Close();
                                }

                                if (File.Exists(resolvedLink + "/product"))
                                {
                                    usbSr            = new StreamReader(resolvedLink + "/product");
                                    UsbProductString = usbSr.ReadToEnd().Trim();
                                    usbSr.Close();
                                }

                                if (File.Exists(resolvedLink + "/serial"))
                                {
                                    usbSr           = new StreamReader(resolvedLink + "/serial");
                                    UsbSerialString = usbSr.ReadToEnd().Trim();
                                    usbSr.Close();
                                }

                                IsUsb = true;
                                break;
                            }
                        }
                    }
                }

                break;

            case PlatformID.Win32NT:
                Usb.UsbDevice usbDevice = null;

                // I have to search for USB disks, floppies and CD-ROMs as separate device types
                foreach (string devGuid in new[]
                {
                    Usb.GuidDevinterfaceFloppy, Usb.GuidDevinterfaceCdrom, Usb.GuidDevinterfaceDisk
                })
                {
                    usbDevice = Usb.FindDrivePath(devicePath, devGuid);
                    if (usbDevice != null)
                    {
                        break;
                    }
                }

                if (usbDevice != null)
                {
                    UsbDescriptors        = usbDevice.BinaryDescriptors;
                    usbVendor             = (ushort)usbDevice.DeviceDescriptor.idVendor;
                    usbProduct            = (ushort)usbDevice.DeviceDescriptor.idProduct;
                    UsbManufacturerString = usbDevice.Manufacturer;
                    UsbProductString      = usbDevice.Product;
                    UsbSerialString       =
                        usbDevice.SerialNumber;     // This is incorrect filled by Windows with SCSI/ATA serial number
                }

                break;

            default:
                IsUsb = false;
                break;
            }
            #endregion USB

            #region FireWire
            if (PlatformId == PlatformID.Linux)
            {
                if (devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
                {
                    string devPath = devicePath.Substring(5);
                    if (Directory.Exists("/sys/block/" + devPath))
                    {
                        string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath);
                        resolvedLink = "/sys" + resolvedLink.Substring(2);
                        if (!string.IsNullOrEmpty(resolvedLink))
                        {
                            while (resolvedLink.Contains("firewire"))
                            {
                                resolvedLink = Path.GetDirectoryName(resolvedLink);
                                if (!File.Exists(resolvedLink + "/model") || !File.Exists(resolvedLink + "/vendor") ||
                                    !File.Exists(resolvedLink + "/guid"))
                                {
                                    continue;
                                }

                                StreamReader fwSr   = new StreamReader(resolvedLink + "/model");
                                string       fwTemp = fwSr.ReadToEnd();
                                uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                              out firewireModel);
                                fwSr.Close();

                                fwSr   = new StreamReader(resolvedLink + "/vendor");
                                fwTemp = fwSr.ReadToEnd();
                                uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                              out firewireVendor);
                                fwSr.Close();

                                fwSr   = new StreamReader(resolvedLink + "/guid");
                                fwTemp = fwSr.ReadToEnd();
                                ulong.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                               out firewireGuid);
                                fwSr.Close();

                                if (File.Exists(resolvedLink + "/model_name"))
                                {
                                    fwSr = new StreamReader(resolvedLink + "/model_name");
                                    FireWireModelName = fwSr.ReadToEnd().Trim();
                                    fwSr.Close();
                                }

                                if (File.Exists(resolvedLink + "/vendor_name"))
                                {
                                    fwSr = new StreamReader(resolvedLink + "/vendor_name");
                                    FireWireVendorName = fwSr.ReadToEnd().Trim();
                                    fwSr.Close();
                                }

                                IsFireWire = true;
                                break;
                            }
                        }
                    }
                }
            }
            // TODO: Implement for other operating systems
            else
            {
                IsFireWire = false;
            }
            #endregion FireWire

            #region PCMCIA
            if (PlatformId == PlatformID.Linux)
            {
                if (devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
                {
                    string devPath = devicePath.Substring(5);
                    if (Directory.Exists("/sys/block/" + devPath))
                    {
                        string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath);
                        resolvedLink = "/sys" + resolvedLink.Substring(2);
                        if (!string.IsNullOrEmpty(resolvedLink))
                        {
                            while (resolvedLink.Contains("/sys/devices"))
                            {
                                resolvedLink = Path.GetDirectoryName(resolvedLink);
                                if (!Directory.Exists(resolvedLink + "/pcmcia_socket"))
                                {
                                    continue;
                                }

                                string[] subdirs = Directory.GetDirectories(resolvedLink + "/pcmcia_socket",
                                                                            "pcmcia_socket*",
                                                                            SearchOption.TopDirectoryOnly);

                                if (subdirs.Length <= 0)
                                {
                                    continue;
                                }

                                string possibleDir = Path.Combine(resolvedLink, "pcmcia_socket", subdirs[0]);
                                if (!File.Exists(possibleDir + "/card_type") || !File.Exists(possibleDir + "/cis"))
                                {
                                    continue;
                                }

                                FileStream cisFs = new FileStream(possibleDir + "/cis", System.IO.FileMode.Open,
                                                                  System.IO.FileAccess.Read);
                                byte[] cisBuf   = new byte[65536];
                                int    cisCount = cisFs.Read(cisBuf, 0, 65536);
                                Cis = new byte[cisCount];
                                Array.Copy(cisBuf, 0, Cis, 0, cisCount);
                                cisFs.Close();

                                IsPcmcia = true;
                                break;
                            }
                        }
                    }
                }
            }
            // TODO: Implement for other operating systems
            else
            {
                IsPcmcia = false;
            }
            #endregion PCMCIA

            if (!scsiSense)
            {
                Inquiry.SCSIInquiry?inquiry = Inquiry.Decode(inqBuf);

                Type = DeviceType.SCSI;
                bool serialSense = ScsiInquiry(out inqBuf, out _, 0x80);
                if (!serialSense)
                {
                    Serial = EVPD.DecodePage80(inqBuf);
                }

                if (inquiry.HasValue)
                {
                    string tmp = StringHandlers.CToString(inquiry.Value.ProductRevisionLevel);
                    if (tmp != null)
                    {
                        Revision = tmp.Trim();
                    }
                    tmp = StringHandlers.CToString(inquiry.Value.ProductIdentification);
                    if (tmp != null)
                    {
                        Model = tmp.Trim();
                    }
                    tmp = StringHandlers.CToString(inquiry.Value.VendorIdentification);
                    if (tmp != null)
                    {
                        Manufacturer = tmp.Trim();
                    }
                    IsRemovable = inquiry.Value.RMB;

                    ScsiType = (PeripheralDeviceTypes)inquiry.Value.PeripheralDeviceType;
                }

                bool atapiSense = AtapiIdentify(out ataBuf, out _);

                if (!atapiSense)
                {
                    Type = DeviceType.ATAPI;
                    Identify.IdentifyDevice?ataId = Identify.Decode(ataBuf);

                    if (ataId.HasValue)
                    {
                        Serial = ataId.Value.SerialNumber;
                    }
                }

                LastError = 0;
                Error     = false;
            }

            if (scsiSense && (IsUsb || IsFireWire) || Manufacturer == "ATA")
            {
                bool ataSense = AtaIdentify(out ataBuf, out _);
                if (!ataSense)
                {
                    Type = DeviceType.ATA;
                    Identify.IdentifyDevice?ataid = Identify.Decode(ataBuf);

                    if (ataid.HasValue)
                    {
                        string[] separated = ataid.Value.Model.Split(' ');

                        if (separated.Length == 1)
                        {
                            Model = separated[0];
                        }
                        else
                        {
                            Manufacturer = separated[0];
                            Model        = separated[separated.Length - 1];
                        }

                        Revision = ataid.Value.FirmwareRevision;
                        Serial   = ataid.Value.SerialNumber;

                        ScsiType = PeripheralDeviceTypes.DirectAccess;

                        if ((ushort)ataid.Value.GeneralConfiguration != 0x848A)
                        {
                            IsRemovable |=
                                (ataid.Value.GeneralConfiguration & Identify.GeneralConfigurationBit.Removable) ==
                                Identify.GeneralConfigurationBit.Removable;
                        }
                        else
                        {
                            IsCompactFlash = true;
                        }
                    }
                }
            }

            if (Type == DeviceType.Unknown)
            {
                Manufacturer = null;
                Model        = null;
                Revision     = null;
                Serial       = null;
            }

            if (IsUsb)
            {
                if (string.IsNullOrEmpty(Manufacturer))
                {
                    Manufacturer = UsbManufacturerString;
                }
                if (string.IsNullOrEmpty(Model))
                {
                    Model = UsbProductString;
                }
                if (string.IsNullOrEmpty(Serial))
                {
                    Serial = UsbSerialString;
                }
                else
                {
                    foreach (char c in Serial.Where(char.IsControl))
                    {
                        Serial = UsbSerialString;
                    }
                }
            }

            if (IsFireWire)
            {
                if (string.IsNullOrEmpty(Manufacturer))
                {
                    Manufacturer = FireWireVendorName;
                }
                if (string.IsNullOrEmpty(Model))
                {
                    Model = FireWireModelName;
                }
                if (string.IsNullOrEmpty(Serial))
                {
                    Serial = $"{firewireGuid:X16}";
                }
                else
                {
                    foreach (char c in Serial.Where(char.IsControl))
                    {
                        Serial = $"{firewireGuid:X16}";
                    }
                }
            }

            // Some optical drives are not getting the correct serial, and IDENTIFY PACKET DEVICE is blocked without
            // administrator privileges
            if (ScsiType != PeripheralDeviceTypes.MultiMediaDevice)
            {
                return;
            }

            bool featureSense = GetConfiguration(out byte[] featureBuffer, out _, 0x0108, MmcGetConfigurationRt.Single,
                                                 Timeout, out _);

            if (featureSense)
            {
                return;
            }

            Features.SeparatedFeatures features = Features.Separate(featureBuffer);
            if (features.Descriptors?.Length != 1 || features.Descriptors[0].Code != 0x0108)
            {
                return;
            }

            Feature_0108?serialFeature = Features.Decode_0108(features.Descriptors[0].Data);

            if (serialFeature is null)
            {
                return;
            }

            Serial = serialFeature.Value.Serial;
        }
        /// <summary>
        ///     Takes the SCSI EVPD part of a device report and prints it as a list key=value pairs to be sequenced by ASP.NET
        ///     in the rendering
        /// </summary>
        /// <param name="pages">EVPD pages</param>
        /// <param name="vendor">SCSI vendor string</param>
        /// <param name="evpdPages">List to put the key=value pairs on</param>
        public static void Report(IEnumerable <ScsiPage> pages, string vendor, ref Dictionary <string, string> evpdPages)
        {
            foreach (ScsiPage evpd in pages)
            {
                string decoded;

                if (evpd.page >= 0x01 &&
                    evpd.page <= 0x7F)
                {
                    decoded = EVPD.DecodeASCIIPage(evpd.value);
                }
                else if (evpd.page == 0x81)
                {
                    decoded = EVPD.PrettifyPage_81(evpd.value);
                }
                else if (evpd.page == 0x82)
                {
                    decoded = EVPD.DecodePage82(evpd.value);
                }
                else if (evpd.page == 0x83)
                {
                    decoded = EVPD.PrettifyPage_83(evpd.value);
                }
                else if (evpd.page == 0x84)
                {
                    decoded = EVPD.PrettifyPage_84(evpd.value);
                }
                else if (evpd.page == 0x85)
                {
                    decoded = EVPD.PrettifyPage_85(evpd.value);
                }
                else if (evpd.page == 0x86)
                {
                    decoded = EVPD.PrettifyPage_86(evpd.value);
                }
                else if (evpd.page == 0x89)
                {
                    decoded = EVPD.PrettifyPage_89(evpd.value);
                }
                else if (evpd.page == 0xB0)
                {
                    decoded = EVPD.PrettifyPage_B0(evpd.value);
                }
                else if (evpd.page == 0xB2)
                {
                    decoded = $"TapeAlert Supported Flags Bitmap: 0x{EVPD.DecodePageB2(evpd.value):X16}<br/>";
                }
                else if (evpd.page == 0xB4)
                {
                    decoded = EVPD.DecodePageB4(evpd.value);
                }
                else if (evpd.page == 0xC0 &&
                         vendor.Trim() == "quantum")
                {
                    decoded = EVPD.PrettifyPage_C0_Quantum(evpd.value);
                }
                else if (evpd.page == 0xC0 &&
                         vendor.Trim() == "seagate")
                {
                    decoded = EVPD.PrettifyPage_C0_Seagate(evpd.value);
                }
                else if (evpd.page == 0xC0 &&
                         vendor.Trim() == "ibm")
                {
                    decoded = EVPD.PrettifyPage_C0_IBM(evpd.value);
                }
                else if (evpd.page == 0xC1 &&
                         vendor.Trim() == "ibm")
                {
                    decoded = EVPD.PrettifyPage_C1_IBM(evpd.value);
                }
                else if ((evpd.page == 0xC0 || evpd.page == 0xC1) &&
                         vendor.Trim() == "certance")
                {
                    decoded = EVPD.PrettifyPage_C0_C1_Certance(evpd.value);
                }
                else if ((evpd.page == 0xC2 || evpd.page == 0xC3 || evpd.page == 0xC4 || evpd.page == 0xC5 ||
                          evpd.page == 0xC6) &&
                         vendor.Trim() == "certance")
                {
                    decoded = EVPD.PrettifyPage_C2_C3_C4_C5_C6_Certance(evpd.value);
                }
                else if ((evpd.page == 0xC0 || evpd.page == 0xC1 || evpd.page == 0xC2 || evpd.page == 0xC3 ||
                          evpd.page == 0xC4 || evpd.page == 0xC5) &&
                         vendor.Trim() == "hp")
                {
                    decoded = EVPD.PrettifyPage_C0_to_C5_HP(evpd.value);
                }
                else if (evpd.page == 0xDF &&
                         vendor.Trim() == "certance")
                {
                    decoded = EVPD.PrettifyPage_DF_Certance(evpd.value);
                }
                else
                {
                    decoded = "Undecoded";
                }

                if (!string.IsNullOrEmpty(decoded))
                {
                    decoded = decoded.Replace("\n", "<br/>");
                }

                evpdPages.Add($"EVPD page {evpd.page:X2}h", decoded);
            }
        }
Beispiel #4
0
        public ScsiInfoViewModel(byte[] scsiInquiryData, Inquiry?scsiInquiry, Dictionary <byte, byte[]> scsiEvpdPages,
                                 Modes.DecodedMode?scsiMode, PeripheralDeviceTypes scsiType, byte[] scsiModeSense6,
                                 byte[] scsiModeSense10, byte[] mmcConfiguration, Window view)
        {
            InquiryData              = scsiInquiryData;
            _scsiModeSense6          = scsiModeSense6;
            _scsiModeSense10         = scsiModeSense10;
            _configuration           = mmcConfiguration;
            _view                    = view;
            ModeSensePages           = new ObservableCollection <ScsiPageModel>();
            EvpdPages                = new ObservableCollection <ScsiPageModel>();
            MmcFeatures              = new ObservableCollection <ScsiPageModel>();
            SaveInquiryBinaryCommand = ReactiveCommand.Create(ExecuteSaveInquiryBinaryCommand);
            SaveInquiryTextCommand   = ReactiveCommand.Create(ExecuteSaveInquiryTextCommand);
            SaveModeSense6Command    = ReactiveCommand.Create(ExecuteSaveModeSense6Command);
            SaveModeSense10Command   = ReactiveCommand.Create(ExecuteSaveModeSense10Command);
            SaveEvpdPageCommand      = ReactiveCommand.Create(ExecuteSaveEvpdPageCommand);
            SaveMmcFeaturesCommand   = ReactiveCommand.Create(ExecuteSaveMmcFeaturesCommand);

            if (InquiryData == null ||
                !scsiInquiry.HasValue)
            {
                return;
            }

            ScsiInquiryText = Decoders.SCSI.Inquiry.Prettify(scsiInquiry);

            if (scsiMode.HasValue)
            {
                ModeSensePages.Add(new ScsiPageModel
                {
                    Page        = "Header",
                    Description = Modes.PrettifyModeHeader(scsiMode.Value.Header, scsiType)
                });

                if (scsiMode.Value.Pages != null)
                {
                    foreach (Modes.ModePage page in scsiMode.Value.Pages.OrderBy(t => t.Page).ThenBy(t => t.Subpage))
                    {
                        string pageNumberText = page.Subpage == 0 ? $"MODE {page.Page:X2}h"
                                                    : $"MODE {page.Page:X2} Subpage {page.Subpage:X2}";

                        string decodedText;

                        switch (page.Page)
                        {
                        case 0x00:
                        {
                            if (scsiType == PeripheralDeviceTypes.MultiMediaDevice &&
                                page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_00_SFF(page.PageResponse);
                            }
                            else
                            {
                                decodedText = "Undecoded";
                            }

                            break;
                        }

                        case 0x01:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = scsiType == PeripheralDeviceTypes.MultiMediaDevice
                                                      ? Modes.PrettifyModePage_01_MMC(page.PageResponse)
                                                      : Modes.PrettifyModePage_01(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x02:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_02(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x03:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_03(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x04:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_04(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x05:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_05(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x06:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_06(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x07:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = scsiType == PeripheralDeviceTypes.MultiMediaDevice
                                                      ? Modes.PrettifyModePage_07_MMC(page.PageResponse)
                                                      : Modes.PrettifyModePage_07(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x08:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_08(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x0A:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_0A(page.PageResponse);
                            }
                            else if (page.Subpage == 1)
                            {
                                decodedText = Modes.PrettifyModePage_0A_S01(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x0B:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_0B(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x0D:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_0D(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x0E:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_0E(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x0F:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_0F(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x10:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = scsiType == PeripheralDeviceTypes.SequentialAccess
                                                      ? Modes.PrettifyModePage_10_SSC(page.PageResponse)
                                                      : Modes.PrettifyModePage_10(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x11:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_11(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x12:
                        case 0x13:
                        case 0x14:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_12_13_14(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x1A:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_1A(page.PageResponse);
                            }
                            else if (page.Subpage == 1)
                            {
                                decodedText = Modes.PrettifyModePage_1A_S01(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x1B:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_1B(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x1C:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = scsiType == PeripheralDeviceTypes.MultiMediaDevice
                                                      ? Modes.PrettifyModePage_1C_SFF(page.PageResponse)
                                                      : Modes.PrettifyModePage_1C(page.PageResponse);
                            }
                            else if (page.Subpage == 1)
                            {
                                decodedText = Modes.PrettifyModePage_1C_S01(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x1D:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_1D(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x21:
                        {
                            if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() ==
                                "CERTANCE")
                            {
                                decodedText = Modes.PrettifyCertanceModePage_21(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x22:
                        {
                            if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() ==
                                "CERTANCE")
                            {
                                decodedText = Modes.PrettifyCertanceModePage_22(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x24:
                        {
                            if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "IBM")
                            {
                                decodedText = Modes.PrettifyIBMModePage_24(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x2A:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_2A(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x2F:
                        {
                            if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "IBM")
                            {
                                decodedText = Modes.PrettifyIBMModePage_2F(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x30:
                        {
                            if (Modes.IsAppleModePage_30(page.PageResponse))
                            {
                                decodedText = "Drive identifies as Apple OEM drive";
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x3B:
                        {
                            if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "HP")
                            {
                                decodedText = Modes.PrettifyHPModePage_3B(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x3C:
                        {
                            if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "HP")
                            {
                                decodedText = Modes.PrettifyHPModePage_3C(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x3D:
                        {
                            if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "IBM")
                            {
                                decodedText = Modes.PrettifyIBMModePage_3D(page.PageResponse);
                            }
                            else if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "HP")
                            {
                                decodedText = Modes.PrettifyHPModePage_3D(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x3E:
                        {
                            if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "FUJITSU")
                            {
                                decodedText = Modes.PrettifyFujitsuModePage_3E(page.PageResponse);
                            }
                            else if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "HP")
                            {
                                decodedText = Modes.PrettifyHPModePage_3E(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        default:
                        {
                            decodedText = "Undecoded";

                            break;
                        }
                        }

                        // TODO: Automatic error reporting
                        if (decodedText == null)
                        {
                            decodedText = "Error decoding page, please open an issue.";
                        }

                        ModeSensePages.Add(new ScsiPageModel
                        {
                            Page        = pageNumberText,
                            Description = decodedText
                        });
                    }
                }
            }

            if (scsiEvpdPages != null)
            {
                foreach (KeyValuePair <byte, byte[]> page in scsiEvpdPages.OrderBy(t => t.Key))
                {
                    string evpdPageTitle = "";
                    string evpdDecodedPage;

                    if (page.Key >= 0x01 &&
                        page.Key <= 0x7F)
                    {
                        evpdPageTitle   = $"ASCII Page {page.Key:X2}h";
                        evpdDecodedPage = EVPD.DecodeASCIIPage(page.Value);
                    }
                    else if (page.Key == 0x80)
                    {
                        evpdPageTitle   = "Unit Serial Number";
                        evpdDecodedPage = EVPD.DecodePage80(page.Value);
                    }
                    else if (page.Key == 0x81)
                    {
                        evpdPageTitle   = "SCSI Implemented operating definitions";
                        evpdDecodedPage = EVPD.PrettifyPage_81(page.Value);
                    }
                    else if (page.Key == 0x82)
                    {
                        evpdPageTitle   = "ASCII implemented operating definitions";
                        evpdDecodedPage = EVPD.DecodePage82(page.Value);
                    }
                    else if (page.Key == 0x83)
                    {
                        evpdPageTitle   = "SCSI Device identification";
                        evpdDecodedPage = EVPD.PrettifyPage_83(page.Value);
                    }
                    else if (page.Key == 0x84)
                    {
                        evpdPageTitle   = "SCSI Software Interface Identifiers";
                        evpdDecodedPage = EVPD.PrettifyPage_84(page.Value);
                    }
                    else if (page.Key == 0x85)
                    {
                        evpdPageTitle   = "SCSI Management Network Addresses";
                        evpdDecodedPage = EVPD.PrettifyPage_85(page.Value);
                    }
                    else if (page.Key == 0x86)
                    {
                        evpdPageTitle   = "SCSI Extended INQUIRY Data";
                        evpdDecodedPage = EVPD.PrettifyPage_86(page.Value);
                    }
                    else if (page.Key == 0x89)
                    {
                        evpdPageTitle   = "SCSI to ATA Translation Layer Data";
                        evpdDecodedPage = EVPD.PrettifyPage_89(page.Value);
                    }
                    else if (page.Key == 0xB0)
                    {
                        evpdPageTitle   = "SCSI Sequential-access Device Capabilities";
                        evpdDecodedPage = EVPD.PrettifyPage_B0(page.Value);
                    }
                    else if (page.Key == 0xB1)
                    {
                        evpdPageTitle   = "Manufacturer-assigned Serial Number";
                        evpdDecodedPage = EVPD.DecodePageB1(page.Value);
                    }
                    else if (page.Key == 0xB2)
                    {
                        evpdPageTitle   = "TapeAlert Supported Flags Bitmap";
                        evpdDecodedPage = $"0x{EVPD.DecodePageB2(page.Value):X16}";
                    }
                    else if (page.Key == 0xB3)
                    {
                        evpdPageTitle   = "Automation Device Serial Number";
                        evpdDecodedPage = EVPD.DecodePageB3(page.Value);
                    }
                    else if (page.Key == 0xB4)
                    {
                        evpdPageTitle   = "Data Transfer Device Element Address";
                        evpdDecodedPage = EVPD.DecodePageB4(page.Value);
                    }
                    else if (page.Key == 0xC0 &&
                             StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant().
                             Trim() == "quantum")
                    {
                        evpdPageTitle   = "Quantum Firmware Build Information page";
                        evpdDecodedPage = EVPD.PrettifyPage_C0_Quantum(page.Value);
                    }
                    else if (page.Key == 0xC0 &&
                             StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant().
                             Trim() == "seagate")
                    {
                        evpdPageTitle   = "Seagate Firmware Numbers page";
                        evpdDecodedPage = EVPD.PrettifyPage_C0_Seagate(page.Value);
                    }
                    else if (page.Key == 0xC0 &&
                             StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant().
                             Trim() == "ibm")
                    {
                        evpdPageTitle   = "IBM Drive Component Revision Levels page";
                        evpdDecodedPage = EVPD.PrettifyPage_C0_IBM(page.Value);
                    }
                    else if (page.Key == 0xC1 &&
                             StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant().
                             Trim() == "ibm")
                    {
                        evpdPageTitle   = "IBM Drive Serial Numbers page";
                        evpdDecodedPage = EVPD.PrettifyPage_C1_IBM(page.Value);
                    }
                    else if ((page.Key == 0xC0 || page.Key == 0xC1) &&
                             StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant().
                             Trim() == "certance")
                    {
                        evpdPageTitle   = "Certance Drive Component Revision Levels page";
                        evpdDecodedPage = EVPD.PrettifyPage_C0_C1_Certance(page.Value);
                    }
                    else if ((page.Key == 0xC2 || page.Key == 0xC3 || page.Key == 0xC4 || page.Key == 0xC5 ||
                              page.Key == 0xC6) &&
                             StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant().
                             Trim() == "certance")
                    {
                        switch (page.Key)
                        {
                        case 0xC2:
                            evpdPageTitle = "Head Assembly Serial Number";

                            break;

                        case 0xC3:
                            evpdPageTitle = "Reel Motor 1 Serial Number";

                            break;

                        case 0xC4:
                            evpdPageTitle = "Reel Motor 2 Serial Number";

                            break;

                        case 0xC5:
                            evpdPageTitle = "Board Serial Number";

                            break;

                        case 0xC6:
                            evpdPageTitle = "Base Mechanical Serial Number";

                            break;
                        }

                        evpdDecodedPage = EVPD.PrettifyPage_C2_C3_C4_C5_C6_Certance(page.Value);
                    }
                    else if ((page.Key == 0xC0 || page.Key == 0xC1 || page.Key == 0xC2 || page.Key == 0xC3 ||
                              page.Key == 0xC4 || page.Key == 0xC5) &&
                             StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant().
                             Trim() == "hp")
                    {
                        switch (page.Key)
                        {
                        case 0xC0:
                            evpdPageTitle = "HP Drive Firmware Revision Levels page:";

                            break;

                        case 0xC1:
                            evpdPageTitle = "HP Drive Hardware Revision Levels page:";

                            break;

                        case 0xC2:
                            evpdPageTitle = "HP Drive PCA Revision Levels page:";

                            break;

                        case 0xC3:
                            evpdPageTitle = "HP Drive Mechanism Revision Levels page:";

                            break;

                        case 0xC4:
                            evpdPageTitle = "HP Drive Head Assembly Revision Levels page:";

                            break;

                        case 0xC5:
                            evpdPageTitle = "HP Drive ACI Revision Levels page:";

                            break;
                        }

                        evpdDecodedPage = EVPD.PrettifyPage_C0_to_C5_HP(page.Value);
                    }
                    else if (page.Key == 0xDF &&
                             StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant().
                             Trim() == "certance")
                    {
                        evpdPageTitle   = "Certance drive status page";
                        evpdDecodedPage = EVPD.PrettifyPage_DF_Certance(page.Value);
                    }
                    else
                    {
                        if (page.Key == 0x00)
                        {
                            continue;
                        }

                        evpdPageTitle   = $"Page {page.Key:X2}h";
                        evpdDecodedPage = "Undecoded";

                        AaruConsole.DebugWriteLine("Device-Info command", "Found undecoded SCSI VPD page 0x{0:X2}",
                                                   page.Key);
                    }

                    EvpdPages.Add(new ScsiPageModel
                    {
                        Page        = evpdPageTitle,
                        Data        = page.Value,
                        Description = evpdDecodedPage
                    });
                }
            }

            if (_configuration != null)
            {
                Features.SeparatedFeatures ftr = Features.Separate(_configuration);

                AaruConsole.DebugWriteLine("Device-Info command", "GET CONFIGURATION length is {0} bytes",
                                           ftr.DataLength);

                AaruConsole.DebugWriteLine("Device-Info command", "GET CONFIGURATION current profile is {0:X4}h",
                                           ftr.CurrentProfile);

                if (ftr.Descriptors != null)
                {
                    foreach (Features.FeatureDescriptor desc in ftr.Descriptors)
                    {
                        string featureNumber = $"Feature {desc.Code:X4}h";
                        string featureDescription;
                        AaruConsole.DebugWriteLine("Device-Info command", "Feature {0:X4}h", desc.Code);

                        switch (desc.Code)
                        {
                        case 0x0000:
                            featureDescription = Features.Prettify_0000(desc.Data);

                            break;

                        case 0x0001:
                            featureDescription = Features.Prettify_0001(desc.Data);

                            break;

                        case 0x0002:
                            featureDescription = Features.Prettify_0002(desc.Data);

                            break;

                        case 0x0003:
                            featureDescription = Features.Prettify_0003(desc.Data);

                            break;

                        case 0x0004:
                            featureDescription = Features.Prettify_0004(desc.Data);

                            break;

                        case 0x0010:
                            featureDescription = Features.Prettify_0010(desc.Data);

                            break;

                        case 0x001D:
                            featureDescription = Features.Prettify_001D(desc.Data);

                            break;

                        case 0x001E:
                            featureDescription = Features.Prettify_001E(desc.Data);

                            break;

                        case 0x001F:
                            featureDescription = Features.Prettify_001F(desc.Data);

                            break;

                        case 0x0020:
                            featureDescription = Features.Prettify_0020(desc.Data);

                            break;

                        case 0x0021:
                            featureDescription = Features.Prettify_0021(desc.Data);

                            break;

                        case 0x0022:
                            featureDescription = Features.Prettify_0022(desc.Data);

                            break;

                        case 0x0023:
                            featureDescription = Features.Prettify_0023(desc.Data);

                            break;

                        case 0x0024:
                            featureDescription = Features.Prettify_0024(desc.Data);

                            break;

                        case 0x0025:
                            featureDescription = Features.Prettify_0025(desc.Data);

                            break;

                        case 0x0026:
                            featureDescription = Features.Prettify_0026(desc.Data);

                            break;

                        case 0x0027:
                            featureDescription = Features.Prettify_0027(desc.Data);

                            break;

                        case 0x0028:
                            featureDescription = Features.Prettify_0028(desc.Data);

                            break;

                        case 0x0029:
                            featureDescription = Features.Prettify_0029(desc.Data);

                            break;

                        case 0x002A:
                            featureDescription = Features.Prettify_002A(desc.Data);

                            break;

                        case 0x002B:
                            featureDescription = Features.Prettify_002B(desc.Data);

                            break;

                        case 0x002C:
                            featureDescription = Features.Prettify_002C(desc.Data);

                            break;

                        case 0x002D:
                            featureDescription = Features.Prettify_002D(desc.Data);

                            break;

                        case 0x002E:
                            featureDescription = Features.Prettify_002E(desc.Data);

                            break;

                        case 0x002F:
                            featureDescription = Features.Prettify_002F(desc.Data);

                            break;

                        case 0x0030:
                            featureDescription = Features.Prettify_0030(desc.Data);

                            break;

                        case 0x0031:
                            featureDescription = Features.Prettify_0031(desc.Data);

                            break;

                        case 0x0032:
                            featureDescription = Features.Prettify_0032(desc.Data);

                            break;

                        case 0x0033:
                            featureDescription = Features.Prettify_0033(desc.Data);

                            break;

                        case 0x0035:
                            featureDescription = Features.Prettify_0035(desc.Data);

                            break;

                        case 0x0037:
                            featureDescription = Features.Prettify_0037(desc.Data);

                            break;

                        case 0x0038:
                            featureDescription = Features.Prettify_0038(desc.Data);

                            break;

                        case 0x003A:
                            featureDescription = Features.Prettify_003A(desc.Data);

                            break;

                        case 0x003B:
                            featureDescription = Features.Prettify_003B(desc.Data);

                            break;

                        case 0x0040:
                            featureDescription = Features.Prettify_0040(desc.Data);

                            break;

                        case 0x0041:
                            featureDescription = Features.Prettify_0041(desc.Data);

                            break;

                        case 0x0042:
                            featureDescription = Features.Prettify_0042(desc.Data);

                            break;

                        case 0x0050:
                            featureDescription = Features.Prettify_0050(desc.Data);

                            break;

                        case 0x0051:
                            featureDescription = Features.Prettify_0051(desc.Data);

                            break;

                        case 0x0080:
                            featureDescription = Features.Prettify_0080(desc.Data);

                            break;

                        case 0x0100:
                            featureDescription = Features.Prettify_0100(desc.Data);

                            break;

                        case 0x0101:
                            featureDescription = Features.Prettify_0101(desc.Data);

                            break;

                        case 0x0102:
                            featureDescription = Features.Prettify_0102(desc.Data);

                            break;

                        case 0x0103:
                            featureDescription = Features.Prettify_0103(desc.Data);

                            break;

                        case 0x0104:
                            featureDescription = Features.Prettify_0104(desc.Data);

                            break;

                        case 0x0105:
                            featureDescription = Features.Prettify_0105(desc.Data);

                            break;

                        case 0x0106:
                            featureDescription = Features.Prettify_0106(desc.Data);

                            break;

                        case 0x0107:
                            featureDescription = Features.Prettify_0107(desc.Data);

                            break;

                        case 0x0108:
                            featureDescription = Features.Prettify_0108(desc.Data);

                            break;

                        case 0x0109:
                            featureDescription = Features.Prettify_0109(desc.Data);

                            break;

                        case 0x010A:
                            featureDescription = Features.Prettify_010A(desc.Data);

                            break;

                        case 0x010B:
                            featureDescription = Features.Prettify_010B(desc.Data);

                            break;

                        case 0x010C:
                            featureDescription = Features.Prettify_010C(desc.Data);

                            break;

                        case 0x010D:
                            featureDescription = Features.Prettify_010D(desc.Data);

                            break;

                        case 0x010E:
                            featureDescription = Features.Prettify_010E(desc.Data);

                            break;

                        case 0x0110:
                            featureDescription = Features.Prettify_0110(desc.Data);

                            break;

                        case 0x0113:
                            featureDescription = Features.Prettify_0113(desc.Data);

                            break;

                        case 0x0142:
                            featureDescription = Features.Prettify_0142(desc.Data);

                            break;

                        default:
                            featureDescription = "Unknown feature";

                            break;
                        }

                        MmcFeatures.Add(new ScsiPageModel
                        {
                            Page        = featureNumber,
                            Description = featureDescription
                        });
                    }
                }
                else
                {
                    AaruConsole.DebugWriteLine("Device-Info command",
                                               "GET CONFIGURATION returned no feature descriptors");
                }
            }
        }
Beispiel #5
0
        public List <ScsiPage> ReportEvpdPages(string vendor)
        {
            AaruConsole.WriteLine("Querying list of SCSI EVPDs...");
            bool sense = _dev.ScsiInquiry(out byte[] buffer, out _, 0x00);

            if (sense)
            {
                return(null);
            }

            byte[] evpdPages = EVPD.DecodePage00(buffer);

            if (evpdPages == null ||
                evpdPages.Length <= 0)
            {
                return(null);
            }

            List <ScsiPage> evpds = new List <ScsiPage>();

            foreach (byte page in evpdPages.Where(page => page != 0x80))
            {
                AaruConsole.WriteLine("Querying SCSI EVPD {0:X2}h...", page);
                sense = _dev.ScsiInquiry(out buffer, out _, page);

                if (sense)
                {
                    continue;
                }

                byte[] empty;

                switch (page)
                {
                case 0x83:
                    buffer = ClearPage83(buffer);

                    break;

                case 0x80:
                    byte[] identify = new byte[512];
                    Array.Copy(buffer, 60, identify, 0, 512);
                    identify = ClearIdentify(identify);
                    Array.Copy(identify, 0, buffer, 60, 512);

                    break;

                case 0xB1:
                case 0xB3:
                    empty = new byte[buffer.Length - 4];
                    Array.Copy(empty, 0, buffer, 4, buffer.Length - 4);

                    break;

                case 0xC1 when vendor == "ibm":
                    empty = new byte[12];
                    Array.Copy(empty, 0, buffer, 4, 12);
                    Array.Copy(empty, 0, buffer, 16, 12);

                    break;

                case 0xC2 when vendor == "certance":
                case 0xC3 when vendor == "certance":
                case 0xC4 when vendor == "certance":
                case 0xC5 when vendor == "certance":
                case 0xC6 when vendor == "certance":
                    Array.Copy(new byte[12], 0, buffer, 4, 12);

                    break;
                }

                var evpd = new ScsiPage
                {
                    page  = page,
                    value = buffer
                };

                evpds.Add(evpd);
            }

            return(evpds.Count > 0 ? evpds : null);
        }
Beispiel #6
0
        internal void LoadData(byte[] scsiInquiryData, CommonTypes.Structs.Devices.SCSI.Inquiry?scsiInquiry,
                               Dictionary <byte, byte[]> scsiEvpdPages, Modes.DecodedMode?scsiMode,
                               PeripheralDeviceTypes scsiType, byte[] scsiModeSense6,
                               byte[] scsiModeSense10,
                               byte[] mmcConfiguration)
        {
            inquiryData   = scsiInquiryData;
            inquiry       = scsiInquiry;
            evpdPages     = scsiEvpdPages;
            mode          = scsiMode;
            type          = scsiType;
            modeSense6    = scsiModeSense6;
            modeSense10   = scsiModeSense10;
            configuration = mmcConfiguration;

            if (inquiryData == null || !inquiry.HasValue)
            {
                return;
            }

            Visible             = true;
            txtScsiInquiry.Text = Inquiry.Prettify(inquiry);

            if (mode.HasValue)
            {
                tabScsiModeSense.Visible = true;

                var modePagesList = new TreeGridItemCollection();

                treeModeSensePages.Columns.Add(new GridColumn {
                    HeaderText = "Page", DataCell = new TextBoxCell(0)
                });

                treeModeSensePages.AllowMultipleSelection = false;
                treeModeSensePages.ShowHeader             = false;
                treeModeSensePages.DataStore = modePagesList;

                modePagesList.Add(new TreeGridItem
                {
                    Values = new object[]
                    {
                        "Header", Modes.PrettifyModeHeader(mode.Value.Header, type)
                    }
                });

                if (mode.Value.Pages != null)
                {
                    foreach (var page in mode.Value.Pages.OrderBy(t => t.Page).ThenBy(t => t.Subpage))
                    {
                        var pageNumberText = page.Subpage == 0
                            ? $"MODE {page.Page:X2}h"
                            : $"MODE {page.Page:X2} Subpage {page.Subpage:X2}";
                        string decodedText;

                        switch (page.Page)
                        {
                        case 0x00:
                        {
                            if (type == PeripheralDeviceTypes.MultiMediaDevice && page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_00_SFF(page.PageResponse);
                            }
                            else
                            {
                                decodedText = "Undecoded";
                            }

                            break;
                        }

                        case 0x01:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = type == PeripheralDeviceTypes.MultiMediaDevice
                                        ? Modes.PrettifyModePage_01_MMC(page.PageResponse)
                                        : Modes.PrettifyModePage_01(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x02:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_02(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x03:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_03(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x04:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_04(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x05:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_05(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x06:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_06(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x07:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = type == PeripheralDeviceTypes.MultiMediaDevice
                                        ? Modes.PrettifyModePage_07_MMC(page.PageResponse)
                                        : Modes.PrettifyModePage_07(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x08:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_08(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x0A:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_0A(page.PageResponse);
                            }
                            else if (page.Subpage == 1)
                            {
                                decodedText = Modes.PrettifyModePage_0A_S01(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x0B:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_0B(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x0D:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_0D(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x0E:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_0E(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x0F:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_0F(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x10:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = type == PeripheralDeviceTypes.SequentialAccess
                                        ? Modes.PrettifyModePage_10_SSC(page.PageResponse)
                                        : Modes.PrettifyModePage_10(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x11:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_11(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x12:
                        case 0x13:
                        case 0x14:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_12_13_14(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x1A:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_1A(page.PageResponse);
                            }
                            else if (page.Subpage == 1)
                            {
                                decodedText = Modes.PrettifyModePage_1A_S01(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x1B:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_1B(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x1C:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = type == PeripheralDeviceTypes.MultiMediaDevice
                                        ? Modes.PrettifyModePage_1C_SFF(page.PageResponse)
                                        : Modes.PrettifyModePage_1C(page.PageResponse);
                            }
                            else if (page.Subpage == 1)
                            {
                                decodedText = Modes.PrettifyModePage_1C_S01(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x1D:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_1D(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x21:
                        {
                            if (StringHandlers.CToString(inquiry?.VendorIdentification).Trim() == "CERTANCE")
                            {
                                decodedText = Modes.PrettifyCertanceModePage_21(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x22:
                        {
                            if (StringHandlers.CToString(inquiry?.VendorIdentification).Trim() == "CERTANCE")
                            {
                                decodedText = Modes.PrettifyCertanceModePage_22(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x24:
                        {
                            if (StringHandlers.CToString(inquiry?.VendorIdentification).Trim() == "IBM")
                            {
                                decodedText = Modes.PrettifyIBMModePage_24(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x2A:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_2A(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x2F:
                        {
                            if (StringHandlers.CToString(inquiry?.VendorIdentification).Trim() == "IBM")
                            {
                                decodedText = Modes.PrettifyIBMModePage_2F(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x30:
                        {
                            if (Modes.IsAppleModePage_30(page.PageResponse))
                            {
                                decodedText = "Drive identifies as Apple OEM drive";
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x3B:
                        {
                            if (StringHandlers.CToString(inquiry?.VendorIdentification).Trim() == "HP")
                            {
                                decodedText = Modes.PrettifyHPModePage_3B(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x3C:
                        {
                            if (StringHandlers.CToString(inquiry?.VendorIdentification).Trim() == "HP")
                            {
                                decodedText = Modes.PrettifyHPModePage_3C(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x3D:
                        {
                            if (StringHandlers.CToString(inquiry?.VendorIdentification).Trim() == "IBM")
                            {
                                decodedText = Modes.PrettifyIBMModePage_3D(page.PageResponse);
                            }
                            else if (StringHandlers.CToString(inquiry?.VendorIdentification).Trim() == "HP")
                            {
                                decodedText = Modes.PrettifyHPModePage_3D(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x3E:
                        {
                            if (StringHandlers.CToString(inquiry?.VendorIdentification).Trim() == "FUJITSU")
                            {
                                decodedText = Modes.PrettifyFujitsuModePage_3E(page.PageResponse);
                            }
                            else if (StringHandlers.CToString(inquiry?.VendorIdentification).Trim() == "HP")
                            {
                                decodedText = Modes.PrettifyHPModePage_3E(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        default:
                        {
                            decodedText = "Undecoded";
                            break;
                        }
                        }

                        // TODO: Automatic error reporting
                        if (decodedText == null)
                        {
                            decodedText = "Error decoding page, please open an issue.";
                        }
                        modePagesList.Add(new TreeGridItem {
                            Values = new object[] { pageNumberText, decodedText }
                        });
                    }
                }
            }

            if (evpdPages != null)
            {
                tabScsiEvpd.Visible      = true;
                treeEvpdPages.ShowHeader = false;

                var evpdPagesList = new TreeGridItemCollection();

                treeEvpdPages.Columns.Add(new GridColumn {
                    HeaderText = "Page", DataCell = new TextBoxCell(0)
                });

                treeEvpdPages.AllowMultipleSelection = false;
                treeEvpdPages.ShowHeader             = false;
                treeEvpdPages.DataStore = evpdPagesList;

                foreach (var page in evpdPages.OrderBy(t => t.Key))
                {
                    var evpdPageTitle   = "";
                    var evpdDecodedPage = "";
                    if (page.Key >= 0x01 && page.Key <= 0x7F)
                    {
                        evpdPageTitle   = $"ASCII Page {page.Key:X2}h";
                        evpdDecodedPage = EVPD.DecodeASCIIPage(page.Value);
                    }
                    else if (page.Key == 0x80)
                    {
                        evpdPageTitle   = "Unit Serial Number";
                        evpdDecodedPage = EVPD.DecodePage80(page.Value);
                    }
                    else if (page.Key == 0x81)
                    {
                        evpdPageTitle   = "SCSI Implemented operating definitions";
                        evpdDecodedPage = EVPD.PrettifyPage_81(page.Value);
                    }
                    else if (page.Key == 0x82)
                    {
                        evpdPageTitle   = "ASCII implemented operating definitions";
                        evpdDecodedPage = EVPD.DecodePage82(page.Value);
                    }
                    else if (page.Key == 0x83)
                    {
                        evpdPageTitle   = "SCSI Device identification";
                        evpdDecodedPage = EVPD.PrettifyPage_83(page.Value);
                    }
                    else if (page.Key == 0x84)
                    {
                        evpdPageTitle   = "SCSI Software Interface Identifiers";
                        evpdDecodedPage = EVPD.PrettifyPage_84(page.Value);
                    }
                    else if (page.Key == 0x85)
                    {
                        evpdPageTitle   = "SCSI Management Network Addresses";
                        evpdDecodedPage = EVPD.PrettifyPage_85(page.Value);
                    }
                    else if (page.Key == 0x86)
                    {
                        evpdPageTitle   = "SCSI Extended INQUIRY Data";
                        evpdDecodedPage = EVPD.PrettifyPage_86(page.Value);
                    }
                    else if (page.Key == 0x89)
                    {
                        evpdPageTitle   = "SCSI to ATA Translation Layer Data";
                        evpdDecodedPage = EVPD.PrettifyPage_89(page.Value);
                    }
                    else if (page.Key == 0xB0)
                    {
                        evpdPageTitle   = "SCSI Sequential-access Device Capabilities";
                        evpdDecodedPage = EVPD.PrettifyPage_B0(page.Value);
                    }
                    else if (page.Key == 0xB1)
                    {
                        evpdPageTitle   = "Manufacturer-assigned Serial Number";
                        evpdDecodedPage = EVPD.DecodePageB1(page.Value);
                    }
                    else if (page.Key == 0xB2)
                    {
                        evpdPageTitle   = "TapeAlert Supported Flags Bitmap";
                        evpdDecodedPage = $"0x{EVPD.DecodePageB2(page.Value):X16}";
                    }
                    else if (page.Key == 0xB3)
                    {
                        evpdPageTitle   = "Automation Device Serial Number";
                        evpdDecodedPage = EVPD.DecodePageB3(page.Value);
                    }
                    else if (page.Key == 0xB4)
                    {
                        evpdPageTitle   = "Data Transfer Device Element Address";
                        evpdDecodedPage = EVPD.DecodePageB4(page.Value);
                    }
                    else if (page.Key == 0xC0 &&
                             StringHandlers.CToString(inquiry.Value.VendorIdentification).ToLowerInvariant().Trim() ==
                             "quantum")
                    {
                        evpdPageTitle   = "Quantum Firmware Build Information page";
                        evpdDecodedPage = EVPD.PrettifyPage_C0_Quantum(page.Value);
                    }
                    else if (page.Key == 0xC0 &&
                             StringHandlers.CToString(inquiry.Value.VendorIdentification).ToLowerInvariant().Trim() ==
                             "seagate")
                    {
                        evpdPageTitle   = "Seagate Firmware Numbers page";
                        evpdDecodedPage = EVPD.PrettifyPage_C0_Seagate(page.Value);
                    }
                    else if (page.Key == 0xC0 &&
                             StringHandlers.CToString(inquiry.Value.VendorIdentification).ToLowerInvariant().Trim() ==
                             "ibm")
                    {
                        evpdPageTitle   = "IBM Drive Component Revision Levels page";
                        evpdDecodedPage = EVPD.PrettifyPage_C0_IBM(page.Value);
                    }
                    else if (page.Key == 0xC1 &&
                             StringHandlers.CToString(inquiry.Value.VendorIdentification).ToLowerInvariant().Trim() ==
                             "ibm")
                    {
                        evpdPageTitle   = "IBM Drive Serial Numbers page";
                        evpdDecodedPage = EVPD.PrettifyPage_C1_IBM(page.Value);
                    }
                    else if ((page.Key == 0xC0 || page.Key == 0xC1) &&
                             StringHandlers.CToString(inquiry.Value.VendorIdentification).ToLowerInvariant().Trim() ==
                             "certance")
                    {
                        evpdPageTitle   = "Certance Drive Component Revision Levels page";
                        evpdDecodedPage = EVPD.PrettifyPage_C0_C1_Certance(page.Value);
                    }
                    else if ((page.Key == 0xC2 || page.Key == 0xC3 || page.Key == 0xC4 || page.Key == 0xC5 ||
                              page.Key == 0xC6) &&
                             StringHandlers.CToString(inquiry.Value.VendorIdentification).ToLowerInvariant().Trim() ==
                             "certance")
                    {
                        switch (page.Key)
                        {
                        case 0xC2:
                            evpdPageTitle = "Head Assembly Serial Number";
                            break;

                        case 0xC3:
                            evpdPageTitle = "Reel Motor 1 Serial Number";
                            break;

                        case 0xC4:
                            evpdPageTitle = "Reel Motor 2 Serial Number";
                            break;

                        case 0xC5:
                            evpdPageTitle = "Board Serial Number";
                            break;

                        case 0xC6:
                            evpdPageTitle = "Base Mechanical Serial Number";
                            break;
                        }

                        evpdDecodedPage = EVPD.PrettifyPage_C2_C3_C4_C5_C6_Certance(page.Value);
                    }
                    else if ((page.Key == 0xC0 || page.Key == 0xC1 || page.Key == 0xC2 || page.Key == 0xC3 ||
                              page.Key == 0xC4 || page.Key == 0xC5) && StringHandlers
                             .CToString(inquiry.Value.VendorIdentification)
                             .ToLowerInvariant().Trim() == "hp")
                    {
                        switch (page.Key)
                        {
                        case 0xC0:
                            evpdPageTitle = "HP Drive Firmware Revision Levels page:";
                            break;

                        case 0xC1:
                            evpdPageTitle = "HP Drive Hardware Revision Levels page:";
                            break;

                        case 0xC2:
                            evpdPageTitle = "HP Drive PCA Revision Levels page:";
                            break;

                        case 0xC3:
                            evpdPageTitle = "HP Drive Mechanism Revision Levels page:";
                            break;

                        case 0xC4:
                            evpdPageTitle = "HP Drive Head Assembly Revision Levels page:";
                            break;

                        case 0xC5:
                            evpdPageTitle = "HP Drive ACI Revision Levels page:";
                            break;
                        }

                        evpdDecodedPage = EVPD.PrettifyPage_C0_to_C5_HP(page.Value);
                    }
                    else if (page.Key == 0xDF &&
                             StringHandlers.CToString(inquiry.Value.VendorIdentification).ToLowerInvariant().Trim() ==
                             "certance")
                    {
                        evpdPageTitle   = "Certance drive status page";
                        evpdDecodedPage = EVPD.PrettifyPage_DF_Certance(page.Value);
                    }
                    else
                    {
                        if (page.Key == 0x00)
                        {
                            continue;
                        }

                        evpdPageTitle   = $"Page {page.Key:X2}h";
                        evpdDecodedPage = "Undecoded";
                        DicConsole.DebugWriteLine("Device-Info command", "Found undecoded SCSI VPD page 0x{0:X2}",
                                                  page.Key);
                    }

                    evpdPagesList.Add(new TreeGridItem
                    {
                        Values = new object[] { evpdPageTitle, evpdDecodedPage, page.Value }
                    });
                }
            }

            if (configuration != null)
            {
                tabMmcFeatures.Visible = true;

                var featuresList = new TreeGridItemCollection();

                treeMmcFeatures.Columns.Add(new GridColumn {
                    HeaderText = "Feature", DataCell = new TextBoxCell(0)
                });

                treeMmcFeatures.AllowMultipleSelection = false;
                treeMmcFeatures.ShowHeader             = false;
                treeMmcFeatures.DataStore = featuresList;

                var ftr = Features.Separate(configuration);

                DicConsole.DebugWriteLine("Device-Info command", "GET CONFIGURATION length is {0} bytes",
                                          ftr.DataLength);
                DicConsole.DebugWriteLine("Device-Info command", "GET CONFIGURATION current profile is {0:X4}h",
                                          ftr.CurrentProfile);
                if (ftr.Descriptors != null)
                {
                    foreach (var desc in ftr.Descriptors)
                    {
                        var    featureNumber = $"Feature {desc.Code:X4}h";
                        string featureDescription;
                        DicConsole.DebugWriteLine("Device-Info command", "Feature {0:X4}h", desc.Code);

                        switch (desc.Code)
                        {
                        case 0x0000:
                            featureDescription = Features.Prettify_0000(desc.Data);
                            break;

                        case 0x0001:
                            featureDescription = Features.Prettify_0001(desc.Data);
                            break;

                        case 0x0002:
                            featureDescription = Features.Prettify_0002(desc.Data);
                            break;

                        case 0x0003:
                            featureDescription = Features.Prettify_0003(desc.Data);
                            break;

                        case 0x0004:
                            featureDescription = Features.Prettify_0004(desc.Data);
                            break;

                        case 0x0010:
                            featureDescription = Features.Prettify_0010(desc.Data);
                            break;

                        case 0x001D:
                            featureDescription = Features.Prettify_001D(desc.Data);
                            break;

                        case 0x001E:
                            featureDescription = Features.Prettify_001E(desc.Data);
                            break;

                        case 0x001F:
                            featureDescription = Features.Prettify_001F(desc.Data);
                            break;

                        case 0x0020:
                            featureDescription = Features.Prettify_0020(desc.Data);
                            break;

                        case 0x0021:
                            featureDescription = Features.Prettify_0021(desc.Data);
                            break;

                        case 0x0022:
                            featureDescription = Features.Prettify_0022(desc.Data);
                            break;

                        case 0x0023:
                            featureDescription = Features.Prettify_0023(desc.Data);
                            break;

                        case 0x0024:
                            featureDescription = Features.Prettify_0024(desc.Data);
                            break;

                        case 0x0025:
                            featureDescription = Features.Prettify_0025(desc.Data);
                            break;

                        case 0x0026:
                            featureDescription = Features.Prettify_0026(desc.Data);
                            break;

                        case 0x0027:
                            featureDescription = Features.Prettify_0027(desc.Data);
                            break;

                        case 0x0028:
                            featureDescription = Features.Prettify_0028(desc.Data);
                            break;

                        case 0x0029:
                            featureDescription = Features.Prettify_0029(desc.Data);
                            break;

                        case 0x002A:
                            featureDescription = Features.Prettify_002A(desc.Data);
                            break;

                        case 0x002B:
                            featureDescription = Features.Prettify_002B(desc.Data);
                            break;

                        case 0x002C:
                            featureDescription = Features.Prettify_002C(desc.Data);
                            break;

                        case 0x002D:
                            featureDescription = Features.Prettify_002D(desc.Data);
                            break;

                        case 0x002E:
                            featureDescription = Features.Prettify_002E(desc.Data);
                            break;

                        case 0x002F:
                            featureDescription = Features.Prettify_002F(desc.Data);
                            break;

                        case 0x0030:
                            featureDescription = Features.Prettify_0030(desc.Data);
                            break;

                        case 0x0031:
                            featureDescription = Features.Prettify_0031(desc.Data);
                            break;

                        case 0x0032:
                            featureDescription = Features.Prettify_0032(desc.Data);
                            break;

                        case 0x0033:
                            featureDescription = Features.Prettify_0033(desc.Data);
                            break;

                        case 0x0035:
                            featureDescription = Features.Prettify_0035(desc.Data);
                            break;

                        case 0x0037:
                            featureDescription = Features.Prettify_0037(desc.Data);
                            break;

                        case 0x0038:
                            featureDescription = Features.Prettify_0038(desc.Data);
                            break;

                        case 0x003A:
                            featureDescription = Features.Prettify_003A(desc.Data);
                            break;

                        case 0x003B:
                            featureDescription = Features.Prettify_003B(desc.Data);
                            break;

                        case 0x0040:
                            featureDescription = Features.Prettify_0040(desc.Data);
                            break;

                        case 0x0041:
                            featureDescription = Features.Prettify_0041(desc.Data);
                            break;

                        case 0x0042:
                            featureDescription = Features.Prettify_0042(desc.Data);
                            break;

                        case 0x0050:
                            featureDescription = Features.Prettify_0050(desc.Data);
                            break;

                        case 0x0051:
                            featureDescription = Features.Prettify_0051(desc.Data);
                            break;

                        case 0x0080:
                            featureDescription = Features.Prettify_0080(desc.Data);
                            break;

                        case 0x0100:
                            featureDescription = Features.Prettify_0100(desc.Data);
                            break;

                        case 0x0101:
                            featureDescription = Features.Prettify_0101(desc.Data);
                            break;

                        case 0x0102:
                            featureDescription = Features.Prettify_0102(desc.Data);
                            break;

                        case 0x0103:
                            featureDescription = Features.Prettify_0103(desc.Data);
                            break;

                        case 0x0104:
                            featureDescription = Features.Prettify_0104(desc.Data);
                            break;

                        case 0x0105:
                            featureDescription = Features.Prettify_0105(desc.Data);
                            break;

                        case 0x0106:
                            featureDescription = Features.Prettify_0106(desc.Data);
                            break;

                        case 0x0107:
                            featureDescription = Features.Prettify_0107(desc.Data);
                            break;

                        case 0x0108:
                            featureDescription = Features.Prettify_0108(desc.Data);
                            break;

                        case 0x0109:
                            featureDescription = Features.Prettify_0109(desc.Data);
                            break;

                        case 0x010A:
                            featureDescription = Features.Prettify_010A(desc.Data);
                            break;

                        case 0x010B:
                            featureDescription = Features.Prettify_010B(desc.Data);
                            break;

                        case 0x010C:
                            featureDescription = Features.Prettify_010C(desc.Data);
                            break;

                        case 0x010D:
                            featureDescription = Features.Prettify_010D(desc.Data);
                            break;

                        case 0x010E:
                            featureDescription = Features.Prettify_010E(desc.Data);
                            break;

                        case 0x0110:
                            featureDescription = Features.Prettify_0110(desc.Data);
                            break;

                        case 0x0113:
                            featureDescription = Features.Prettify_0113(desc.Data);
                            break;

                        case 0x0142:
                            featureDescription = Features.Prettify_0142(desc.Data);
                            break;

                        default:
                            featureDescription = "Unknown feature";
                            break;
                        }

                        featuresList.Add(new TreeGridItem {
                            Values = new object[] { featureNumber, featureDescription }
                        });
                    }
                }
                else
                {
                    DicConsole.DebugWriteLine("Device-Info command",
                                              "GET CONFIGURATION returned no feature descriptors");
                }
            }

            Invalidate();
        }