From 16cbf58dd4a481dc02cad952df225f57bed6b8df Mon Sep 17 00:00:00 2001 From: chudov Date: Wed, 26 Nov 2008 18:57:40 +0000 Subject: [PATCH] Initial checkin --- Bwg.Hardware/Bwg.Hardware.csproj | 112 + Bwg.Hardware/HardwareDevice.cs | 209 + Bwg.Hardware/HardwareManager.cs | 209 + Bwg.Hardware/Properties/AssemblyInfo.cs | 35 + Bwg.Logging/Bwg.Logging.csproj | 121 + Bwg.Logging/ConsoleSink.cs | 47 + Bwg.Logging/FileSink.cs | 67 + Bwg.Logging/Logger.cs | 213 + Bwg.Logging/MemorySink.cs | 114 + Bwg.Logging/MessageAddedArgs.cs | 49 + Bwg.Logging/Properties/AssemblyInfo.cs | 35 + Bwg.Logging/Sink.cs | 43 + Bwg.Logging/TeeSink.cs | 59 + Bwg.Logging/UserMessage.cs | 117 + Bwg.Scsi/AtipInfo.cs | 150 + Bwg.Scsi/Bwg.Scsi.csproj | 159 + Bwg.Scsi/CapacityDescriptor.cs | 109 + Bwg.Scsi/Command.cs | 236 ++ Bwg.Scsi/Defs.cs | 81 + Bwg.Scsi/Device.cs | 3686 +++++++++++++++++ Bwg.Scsi/DeviceInfo.cs | 160 + Bwg.Scsi/DeviceManager.cs | 228 + Bwg.Scsi/DeviceManagerRescanArgs.cs | 14 + Bwg.Scsi/DiskInformation.cs | 409 ++ Bwg.Scsi/EventStatusNotification.cs | 171 + Bwg.Scsi/Feature.cs | 438 ++ Bwg.Scsi/FeatureList.cs | 64 + Bwg.Scsi/FileReader.cs | 142 + Bwg.Scsi/FormatParameterList.cs | 343 ++ Bwg.Scsi/HeaderData.cs | 142 + Bwg.Scsi/InitializationPattern.cs | 77 + Bwg.Scsi/InquiryResult.cs | 128 + Bwg.Scsi/MinuteSecondFrame.cs | 309 ++ Bwg.Scsi/ModePage.cs | 174 + Bwg.Scsi/ModeTable.cs | 108 + Bwg.Scsi/OpcTableEntry.cs | 60 + Bwg.Scsi/Performance.cs | 84 + Bwg.Scsi/PerformanceList.cs | 170 + Bwg.Scsi/Properties/AssemblyInfo.cs | 35 + Bwg.Scsi/Result.cs | 205 + Bwg.Scsi/SpeedDescriptor.cs | 115 + Bwg.Scsi/SpeedDescriptorList.cs | 96 + Bwg.Scsi/SubheaderData.cs | 121 + Bwg.Scsi/TocEntry.cs | 80 + Bwg.Scsi/TrackInformation.cs | 204 + Bwg.Scsi/WinDev.cs | 190 + Bwg.Scsi/WriteBuffer.cs | 137 + Bwg.Scsi/WriteBufferPool.cs | 284 ++ Bwg.Scsi/WriteBufferStream.cs | 267 ++ Bwg.Scsi/WriteParameterModePage.cs | 584 +++ .../CUETools.ConsoleRipper.csproj | 101 + CUETools.Ripper.Console/Program.cs | 146 + .../Properties/AssemblyInfo.cs | 33 + .../CUETools.Ripper.SCSI.csproj | 109 + .../Properties/AssemblyInfo.cs | 35 + CUETools.Ripper.SCSI/SCSIDrive.cs | 452 ++ 56 files changed, 12266 insertions(+) create mode 100644 Bwg.Hardware/Bwg.Hardware.csproj create mode 100644 Bwg.Hardware/HardwareDevice.cs create mode 100644 Bwg.Hardware/HardwareManager.cs create mode 100644 Bwg.Hardware/Properties/AssemblyInfo.cs create mode 100644 Bwg.Logging/Bwg.Logging.csproj create mode 100644 Bwg.Logging/ConsoleSink.cs create mode 100644 Bwg.Logging/FileSink.cs create mode 100644 Bwg.Logging/Logger.cs create mode 100644 Bwg.Logging/MemorySink.cs create mode 100644 Bwg.Logging/MessageAddedArgs.cs create mode 100644 Bwg.Logging/Properties/AssemblyInfo.cs create mode 100644 Bwg.Logging/Sink.cs create mode 100644 Bwg.Logging/TeeSink.cs create mode 100644 Bwg.Logging/UserMessage.cs create mode 100644 Bwg.Scsi/AtipInfo.cs create mode 100644 Bwg.Scsi/Bwg.Scsi.csproj create mode 100644 Bwg.Scsi/CapacityDescriptor.cs create mode 100644 Bwg.Scsi/Command.cs create mode 100644 Bwg.Scsi/Defs.cs create mode 100644 Bwg.Scsi/Device.cs create mode 100644 Bwg.Scsi/DeviceInfo.cs create mode 100644 Bwg.Scsi/DeviceManager.cs create mode 100644 Bwg.Scsi/DeviceManagerRescanArgs.cs create mode 100644 Bwg.Scsi/DiskInformation.cs create mode 100644 Bwg.Scsi/EventStatusNotification.cs create mode 100644 Bwg.Scsi/Feature.cs create mode 100644 Bwg.Scsi/FeatureList.cs create mode 100644 Bwg.Scsi/FileReader.cs create mode 100644 Bwg.Scsi/FormatParameterList.cs create mode 100644 Bwg.Scsi/HeaderData.cs create mode 100644 Bwg.Scsi/InitializationPattern.cs create mode 100644 Bwg.Scsi/InquiryResult.cs create mode 100644 Bwg.Scsi/MinuteSecondFrame.cs create mode 100644 Bwg.Scsi/ModePage.cs create mode 100644 Bwg.Scsi/ModeTable.cs create mode 100644 Bwg.Scsi/OpcTableEntry.cs create mode 100644 Bwg.Scsi/Performance.cs create mode 100644 Bwg.Scsi/PerformanceList.cs create mode 100644 Bwg.Scsi/Properties/AssemblyInfo.cs create mode 100644 Bwg.Scsi/Result.cs create mode 100644 Bwg.Scsi/SpeedDescriptor.cs create mode 100644 Bwg.Scsi/SpeedDescriptorList.cs create mode 100644 Bwg.Scsi/SubheaderData.cs create mode 100644 Bwg.Scsi/TocEntry.cs create mode 100644 Bwg.Scsi/TrackInformation.cs create mode 100644 Bwg.Scsi/WinDev.cs create mode 100644 Bwg.Scsi/WriteBuffer.cs create mode 100644 Bwg.Scsi/WriteBufferPool.cs create mode 100644 Bwg.Scsi/WriteBufferStream.cs create mode 100644 Bwg.Scsi/WriteParameterModePage.cs create mode 100644 CUETools.Ripper.Console/CUETools.ConsoleRipper.csproj create mode 100644 CUETools.Ripper.Console/Program.cs create mode 100644 CUETools.Ripper.Console/Properties/AssemblyInfo.cs create mode 100644 CUETools.Ripper.SCSI/CUETools.Ripper.SCSI.csproj create mode 100644 CUETools.Ripper.SCSI/Properties/AssemblyInfo.cs create mode 100644 CUETools.Ripper.SCSI/SCSIDrive.cs diff --git a/Bwg.Hardware/Bwg.Hardware.csproj b/Bwg.Hardware/Bwg.Hardware.csproj new file mode 100644 index 0000000..fe3a896 --- /dev/null +++ b/Bwg.Hardware/Bwg.Hardware.csproj @@ -0,0 +1,112 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {B75FA7AD-968E-4990-B342-1B4B17C850DF} + Library + Properties + Bwg.Hardware + Bwg.Hardware + + + + + + + + + + + 2.0 + + + v3.5 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + bin\Debug\Bwg.Hardware.XML + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x64\Debug\ + DEBUG;TRACE + bin\Debug\Bwg.Hardware.XML + false + full + x64 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + + + true + bin\x86\Debug\ + DEBUG;TRACE + bin\Debug\Bwg.Hardware.XML + true + full + x86 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Bwg.Hardware/HardwareDevice.cs b/Bwg.Hardware/HardwareDevice.cs new file mode 100644 index 0000000..9d0b03f --- /dev/null +++ b/Bwg.Hardware/HardwareDevice.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Hardware +{ + /// + /// This class represents a hardware device as extracted from the registry. + /// + public class HardwareDevice + { + #region private member variables + private string m_name; + private string m_desc; + private string m_class; + private Guid m_class_guid; + private string m_key_name ; + private string m_location; + private string[] m_hardware; + #endregion + + #region constructor + /// + /// The constructor for a device. + /// + /// the name of the registry key that contains info about this device + public HardwareDevice(string key) + { + m_key_name = key; + m_name = string.Empty ; + m_desc = string.Empty ; + m_location = string.Empty; + + m_hardware = new string[0]; + } + #endregion + + #region public methods + /// + /// Add a new hardware address to the device + /// + /// the new address to add to the device + public void AddHardware(string addr) + { + int count = m_hardware.GetLength(0); + Array.Resize(ref m_hardware, count + 1); + m_hardware[count] = addr; + } + #endregion + + #region public properties + + /// + /// The name of the key where this information was extracted + /// + public string KeyName + { + get { return m_key_name; } + } + + /// + /// This property is the (friendly) name of the device + /// + public string Name + { + get + { + if (m_name != string.Empty) + return m_name; + + if (m_desc != string.Empty) + return m_desc; + + return m_key_name; + } + set + { + m_name = value; + } + } + + /// + /// This property is the description of the device + /// + public string Description + { + get + { + return m_desc; + } + set + { + m_desc = value; + } + } + + /// + /// This property is the class that the device belong to + /// + public string Class + { + get + { + return m_class; + } + set + { + m_class = value; + } + } + + /// + /// This property is the GUID for the class that the device belongs to + /// + public Guid ClassGUID + { + get + { + return m_class_guid; + } + set + { + m_class_guid = value; + } + } + + /// + /// This property is the location of the device + /// + public string Location + { + get { return m_location; } + set { m_location = value; } + } + + /// + /// This class is the hardware addresses assocaited with the device + /// + public string[] Hardware + { + get + { + return m_hardware; + } + } + + /// + /// This property returns true if the device is an IDE device + /// + public bool IsIde + { + get + { + return HardwareStartsWith("IDE"); + } + } + + /// + /// This property returns true if this is a USB device + /// + public bool IsUsb + { + get + { + return HardwareStartsWith("USB") ; + } + } + + /// + /// This property returns true if this is a Firewire device + /// + public bool IsFirewire + { + get + { + return HardwareStartsWith("SBP2"); + } + } + + /// + /// This property returns true if this is a SCSI device + /// + public bool IsScsi + { + get + { + return HardwareStartsWith("SCSI"); + } + } + #endregion + + #region private methods + /// + /// This method returns true if the hardware address start with the string given + /// + /// the string to start with + /// true if the string begins with the right value, otherwise false + public bool HardwareStartsWith(string begin) + { + foreach (string str in m_hardware) + { + if (str.StartsWith(begin)) + return true; + } + return false; + } + #endregion + } +} diff --git a/Bwg.Hardware/HardwareManager.cs b/Bwg.Hardware/HardwareManager.cs new file mode 100644 index 0000000..e3a0f9f --- /dev/null +++ b/Bwg.Hardware/HardwareManager.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Text; +using System.IO; +using System.Security; +using Microsoft.Win32; + +namespace Bwg.Hardware +{ + /// + /// This class is the manager that reads the registry and determines which devices exist + /// + public class HardwareManager : IEnumerable, IEnumerable + { + #region private member variables + private IList m_devices; + #endregion + + #region constructors + /// + /// The constructor for the device manager, creates a list for devices to be added. + /// + public HardwareManager() + { + m_devices = new List(); + } + #endregion + + /// + /// Return an enumerator for iterating over all of the tracks on the + /// disk. + /// + /// iterator + public IEnumerator GetEnumerator() + { + return m_devices.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return m_devices.GetEnumerator(); + } + + /// + /// Open a given machine, or the default machine if no machine is given + /// + /// + /// + public bool OpenMachine(string keyname) + { + if (keyname == string.Empty) + keyname = "System\\CurrentControlSet\\Enum"; + + RegistryKey key = Registry.LocalMachine.OpenSubKey(keyname); + if (key == null) + return false; + + CheckForHardware(key); + key.Close(); + return true; + } + + /// + /// Find a specific device given its class, vendor, and product + /// + /// the class of the device + /// the vendor id of the device + /// the product id of the device + /// + public HardwareDevice FindDevice(string classname, string vendor, string product) + { + foreach (HardwareDevice dev in m_devices) + { + if (dev.Class != classname) + continue; + + if (dev.Name.Contains(vendor) && dev.Name.Contains(product)) + { + if (dev.IsUsb && dev.Location.Length == 0) + dev.Location = FindUsbLocation(dev); + + return dev; + } + } + return null; + } + + #region private methods + + private string FindUsbLocation(HardwareDevice cddev) + { + string result = string.Empty; + int index = cddev.KeyName.LastIndexOf('&'); + if (index == -1) + return result; + + string devname = cddev.KeyName.Substring(0, index); + foreach (HardwareDevice dev in m_devices) + { + if (dev.Class == "USB" && dev.KeyName == devname) + return dev.Location; + } + + return result; + } + + private static bool FindClassString(string s1) + { + return s1 == "Class"; + } + + private static bool FindClassGUIDString(string s1) + { + return s1 == "ClassGUID"; + } + + private void CreateDevice(RegistryKey key) + { + string keyname = key.ToString(); + string onekey ; + HardwareDevice dev; + + try + { + onekey = Path.GetFileName(keyname); + } + catch (ArgumentException) + { + return; + } + + dev = new HardwareDevice(onekey); + object value; + + value = key.GetValue("Class"); + if (value != null) + { + if (value is string) + dev.Class = (string)value; + else if (value is string[]) + { + dev.Class = ((string[])value)[0]; + } + } + + value = key.GetValue("ClassGUID"); + if (value != null && value is string) + dev.ClassGUID = new Guid((string)value); + + value = key.GetValue("DeviceDesc"); + if (value != null && value is string) + dev.Description = (string)value; + + value = key.GetValue("FriendlyName"); + if (value != null && value is string) + dev.Name = (string)value; + + value = key.GetValue("LocationInformation"); + if (value != null && value is string) + dev.Location = (string)value; + + value = key.GetValue("HardwareID"); + if (value != null) + { + if (value is string) + dev.AddHardware((string)value); + else if (value is string[]) + { + foreach (string str in (string[])value) + dev.AddHardware(str); + } + } + + m_devices.Add(dev); + } + + private void CheckForHardware(RegistryKey key) + { + string[] values = key.GetValueNames(); + if (Array.Find(values, FindClassString) != null && Array.Find(values, FindClassGUIDString) != null) + { + // This is a device + CreateDevice(key); + } + else + { + values = key.GetSubKeyNames(); + foreach (string str in values) + { + try + { + RegistryKey subkey = key.OpenSubKey(str); + if (subkey != null) + { + CheckForHardware(subkey); + subkey.Close(); + } + } + catch (SecurityException) + { + } + } + } + } + + #endregion + } +} diff --git a/Bwg.Hardware/Properties/AssemblyInfo.cs b/Bwg.Hardware/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..4e44a4a --- /dev/null +++ b/Bwg.Hardware/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Bwg.Hardware")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("BwgSoftware")] +[assembly: AssemblyProduct("BwgBurn")] +[assembly: AssemblyCopyright("Copyright © 2006 by Jack W. Griffin, Jr.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("de5dc145-3520-47ee-8988-205a142c6177")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("0.0.7.1")] +[assembly: AssemblyFileVersion("0.0.7.1")] diff --git a/Bwg.Logging/Bwg.Logging.csproj b/Bwg.Logging/Bwg.Logging.csproj new file mode 100644 index 0000000..13882ad --- /dev/null +++ b/Bwg.Logging/Bwg.Logging.csproj @@ -0,0 +1,121 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {F2DFEB00-BB35-4665-85EA-CB8C7729A6B7} + Library + Properties + Bwg.Logging + Bwg.Logging + + + + + + + + + + + 2.0 + + + v3.5 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + bin\Debug\Bwg.Logging.XML + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + bin\Release\Bwg.Logging.XML + + + true + bin\x64\Debug\ + DEBUG;TRACE + bin\Debug\Bwg.Logging.XML + false + full + x64 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + + + bin\x64\Release\ + TRACE + bin\Release\Bwg.Logging.XML + true + pdbonly + x64 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + + + true + bin\x86\Debug\ + DEBUG;TRACE + bin\Debug\Bwg.Logging.XML + true + full + x86 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + + + bin\x86\Release\ + TRACE + bin\Release\Bwg.Logging.XML + true + pdbonly + x86 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Bwg.Logging/ConsoleSink.cs b/Bwg.Logging/ConsoleSink.cs new file mode 100644 index 0000000..45e416b --- /dev/null +++ b/Bwg.Logging/ConsoleSink.cs @@ -0,0 +1,47 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Logging +{ + /// + /// A message sink class that writes messages to the console + /// + public class ConsoleSink : Sink + { + /// + /// Log the message to the console + /// + /// the message + public override void LogMessage(UserMessage m) + { + if (m.Code == 0) + System.Console.WriteLine(m.MType.ToString() + ": " + m.Text); + else + System.Console.WriteLine(m.MType.ToString() + " " + m.Code.ToString() + ": " + m.Text); + } + } +} diff --git a/Bwg.Logging/FileSink.cs b/Bwg.Logging/FileSink.cs new file mode 100644 index 0000000..c722d43 --- /dev/null +++ b/Bwg.Logging/FileSink.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO ; + +namespace Bwg.Logging +{ + /// + /// This class is a message sink that writes the message to a file + /// + public class FileSink : Sink, IDisposable + { + #region private member variables + private TextWriter m_writer; + #endregion + + #region constructor + /// + /// Create a new file sink given the name of the output file + /// + /// the output file + public FileSink(string filename) + { + try + { + m_writer = new StreamWriter(filename); + } + catch (Exception) + { + m_writer = null; + } + } + #endregion + + #region public methods + /// + /// Dispose of the class, close the file + /// + public void Dispose() + { + Close(); + } + + /// + /// Close the file + /// + public void Close() + { + if (m_writer != null) + { + m_writer.Flush(); + m_writer.Close(); + } + } + + /// + /// Log the message + /// + /// the message to log + public override void LogMessage(UserMessage m) + { + if (m_writer != null) + m_writer.WriteLine(m.ToString()); + } + #endregion + } +} diff --git a/Bwg.Logging/Logger.cs b/Bwg.Logging/Logger.cs new file mode 100644 index 0000000..e400137 --- /dev/null +++ b/Bwg.Logging/Logger.cs @@ -0,0 +1,213 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; + +namespace Bwg.Logging +{ + /// + /// This class allows for the logging of messages + /// + public class Logger + { + /// + /// This is used to lock the logger when multiple threads are + /// trying to log messages concurrently + /// + private Object m_lock_object; + + /// + /// This is a map from message type to sinks + /// + private IDictionary m_sinks; + + /// + /// This is a map from the message type to the level that is being filtered. Anything + /// below the value found here is not displayed + /// + private IDictionary m_level; + + /// + /// Create the logging object + /// + public Logger() + { + m_lock_object = new Object(); + m_sinks = new Dictionary(); + m_level = new Dictionary(); + } + + /// + /// Assocated a sink with a given type of message + /// + /// the category of message + /// the sink for this category of message + public void SetSink(UserMessage.Category c, Sink s) + { + lock (m_lock_object) + { + m_sinks[c] = s; + } + } + + /// + /// Return the sink given the type of message + /// + /// the type of sink + /// the sink associated with the message type + public Sink GetSink(UserMessage.Category c) + { + Sink s = null; + + lock (m_lock_object) + { + m_sinks.TryGetValue(c, out s); + } + + return s; + } + + + /// + /// Sets a level, below which messages are filtered + /// + /// the category to filter + /// the level to display + public void SetLevel(UserMessage.Category c, int level) + { + lock (m_lock_object) + { + m_level[c] = level; + } + } + + /// + /// Given the category, this method returns the current + /// level for this type of message + /// + /// the type of message + /// the current level + public int GetLevel(UserMessage.Category c) + { + int ret = 0; + + lock (m_lock_object) + { + if (m_level.ContainsKey(c)) + ret = m_level[c]; + } + + return ret; + } + + + /// + /// Remove the sink associated with a specific category of message. + /// + /// the category of message + public void RemoveSink(UserMessage.Category c) + { + lock (m_lock_object) + { + m_sinks.Remove(c); + } + } + + /// + /// This function returns TRUE if the logger has a message sink for the given + /// category of message. Otherwise it returns FALSE. + /// + /// the category of interest + /// true if a sink is present, false otherwise + public bool HasSink(UserMessage.Category c) + { + return m_sinks.Keys.Contains(c); + } + + /// + /// Log a message + /// + /// the message to log + public void LogMessage(UserMessage m) + { + lock (m_lock_object) + { + if (m_sinks.ContainsKey(m.MType)) + { + int level = 0; + + if (m_level.ContainsKey(m.MType)) + level = m_level[m.MType]; + + if (m.Level <= level) + { + Sink s = m_sinks[m.MType]; + s.LogMessage(m); + } + } + } + } + + /// + /// Dump a data buffer to the log file, used for debugging + /// + /// the log level + /// the title + /// the buffer + /// the buffer size + public void DumpBuffer(uint loglevel, string title, IntPtr buffer, int size) + { + UserMessage m; + + m = new UserMessage(UserMessage.Category.Debug, loglevel, "Dumping data for structure '" + title + "'"); + LogMessage(m); + + int index = 0; + int linebytes = 0; + string str = string.Empty; + while (index < size) + { + if (linebytes == 16) + { + m = new UserMessage(UserMessage.Category.Debug, loglevel, str); + LogMessage(m); + linebytes = 0; + str = string.Empty; + } + + byte b = Marshal.ReadByte(buffer, index++); + str += b.ToString("X2") + " "; + linebytes++; + } + + if (linebytes != 0) + { + m = new UserMessage(UserMessage.Category.Debug, loglevel, str); + LogMessage(m); + } + } + } +} diff --git a/Bwg.Logging/MemorySink.cs b/Bwg.Logging/MemorySink.cs new file mode 100644 index 0000000..a5b7074 --- /dev/null +++ b/Bwg.Logging/MemorySink.cs @@ -0,0 +1,114 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Logging +{ + /// + /// This class is a message sink that stores all of the messages + /// in a list. + /// + public class MemorySink : Sink + { + private Object m_lock; + private IList m_list ; + + /// + /// + /// + public EventHandler MessageAdded; + + /// + /// + /// + public MemorySink() + { + m_lock = new Object(); + m_list = new List(); + } + + /// + /// + /// + /// + public int GetMessageCount() + { + int cnt; + + lock (m_lock) + { + cnt = m_list.Count; + } + return cnt; + } + + /// + /// + /// + /// + /// + public UserMessage GetMessage(int n) + { + UserMessage m; + + lock (m_lock) + { + m = m_list[n]; + } + + return m; + } + + /// + /// + /// + /// + public override void LogMessage(UserMessage m) + { + lock (m_lock) + { + m_list.Add(m); + } + + if (MessageAdded != null) + { + MessageAddedArgs args = new MessageAddedArgs(m); + MessageAdded(this, args); + } + } + + /// + /// + /// + public void Clear() + { + lock (m_lock) + { + m_list.Clear(); + } + } + } +} diff --git a/Bwg.Logging/MessageAddedArgs.cs b/Bwg.Logging/MessageAddedArgs.cs new file mode 100644 index 0000000..4da44d8 --- /dev/null +++ b/Bwg.Logging/MessageAddedArgs.cs @@ -0,0 +1,49 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Logging +{ + /// + /// + /// + public class MessageAddedArgs : EventArgs + { + /// + /// + /// + public UserMessage AddedMessage; + + /// + /// + /// + /// + public MessageAddedArgs(UserMessage m) + { + AddedMessage = m; + } + } +} diff --git a/Bwg.Logging/Properties/AssemblyInfo.cs b/Bwg.Logging/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c21ddab --- /dev/null +++ b/Bwg.Logging/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Bwg.Logging")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("BwgSoftware")] +[assembly: AssemblyProduct("BwgBurn")] +[assembly: AssemblyCopyright("Copyright © 2006 by Jack W. Griffin, Jr.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("216f12e9-2978-457c-95d6-32b4b87a361d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("0.0.7.1")] +[assembly: AssemblyFileVersion("0.0.7.1")] diff --git a/Bwg.Logging/Sink.cs b/Bwg.Logging/Sink.cs new file mode 100644 index 0000000..6261202 --- /dev/null +++ b/Bwg.Logging/Sink.cs @@ -0,0 +1,43 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Logging +{ + /// + /// An abstract class that provides a place to send messages meant for the + /// user. A class derived from this class is used to allow for GUI versus + /// console applications + /// + abstract public class Sink + { + /// + /// This method sends a message to a sink. + /// + /// + abstract public void LogMessage(UserMessage m); + } +} diff --git a/Bwg.Logging/TeeSink.cs b/Bwg.Logging/TeeSink.cs new file mode 100644 index 0000000..e6e19ad --- /dev/null +++ b/Bwg.Logging/TeeSink.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Logging +{ + /// + /// This class is a message sink that forwards a message to a set of receiving sinks + /// + public class TeeSink : Sink + { + #region private member variables + /// + /// The list of sinks to send the message to + /// + IList m_sinks; + #endregion + + #region constructor + /// + /// The constructor to creat the object + /// + public TeeSink() + { + m_sinks = new List(); + } + #endregion + + #region public methods + /// + /// Add the sink to the list of receiving sinks + /// + /// the sink to add + public void AddSink(Sink s) + { + m_sinks.Add(s); + } + + /// + /// Remove the sink to the list of receiving sinks + /// + /// + public void RemoveSink(Sink s) + { + m_sinks.Remove(s); + } + + /// + /// Log a message to the receiving sinks + /// + /// the message + public override void LogMessage(UserMessage m) + { + foreach (Sink s in m_sinks) + s.LogMessage(m); + } + #endregion + } +} diff --git a/Bwg.Logging/UserMessage.cs b/Bwg.Logging/UserMessage.cs new file mode 100644 index 0000000..32193b1 --- /dev/null +++ b/Bwg.Logging/UserMessage.cs @@ -0,0 +1,117 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Logging +{ + /// + /// A message to be logged to the u + /// + public class UserMessage + { + #region Public Types + /// + /// Message types + /// + public enum Category + { + /// + /// An error occurred + /// + Error, + + /// + /// A warning occurred + /// + Warning, + + /// + /// Information about the process + /// + Info, + + /// + /// Debugging information + /// + Debug + } ; + #endregion + + #region Public Data Members + /// + /// The category for the message (error, warning, info, debug) + /// + public readonly Category MType ; + + /// + /// The numeric error code + /// + public readonly uint Code ; + + /// + /// The text for the error message + /// + public readonly string Text; + + /// + /// The level for the message, used to filter out messages + /// + public readonly uint Level; + + /// + /// This member contains a time stamp for the message + /// + public readonly DateTime When; + #endregion + + #region constructor + /// + /// Constructor for a message + /// + /// Category of the message + /// Level of the message + /// Text of the message + public UserMessage(Category t, uint level, string s) + { + MType = t; + Text = s; + Level = level; + When = DateTime.Now; + } + #endregion + + #region public member functions + /// + /// This method converts a user message to a single string + /// + /// the string that represnts the message + public override string ToString() + { + return When.ToLongDateString() + " " + When.ToLongTimeString() + " : " + MType.ToString() + " : Level " + Level.ToString() + " : " + Text; + } + #endregion + } +} diff --git a/Bwg.Scsi/AtipInfo.cs b/Bwg.Scsi/AtipInfo.cs new file mode 100644 index 0000000..b9a84f9 --- /dev/null +++ b/Bwg.Scsi/AtipInfo.cs @@ -0,0 +1,150 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for DotNet +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; + +namespace Bwg.Scsi +{ + /// + /// This class represents the ATIP information read from the disk + /// + public class AtipInfo : Result + { + /// + /// The target write power for the disk + /// + public readonly byte IndicativeTargetWritingPower; + + /// + /// + /// + public readonly byte ReferenceSpeed; + + /// + /// If true, this is a double density CD + /// + public readonly bool IsDDCD; + + /// + /// If true, this disk is for unrestricted use + /// + public readonly bool UnrestrictedUse; + + /// + /// If true, the media is CDRW, otherwise it is CDR + /// + public readonly bool MediaCDRW; + + /// + /// The CD/RW media subtype + /// + public readonly byte MediaSubtype; + + /// + /// + /// + public readonly bool A1Valid; + + /// + /// + /// + public readonly bool A2Valid; + + /// + /// + /// + public readonly bool A3Valid; + + /// + /// The ATIP value for the starting leadin time + /// + public readonly MinuteSecondFrame StartTimeOfLeadin; + + /// + /// This is the address for the last possible leadout + /// + public readonly MinuteSecondFrame LastPossibleStartOfLeadout; + + /// + /// The data from the A1 field + /// + public readonly byte[] A1Values; + + /// + /// The data from the A1 field + /// + public readonly byte[] A2Values; + + /// + /// The data from the A3 field + /// + public readonly byte[] A3Values; + + /// + /// This constructs the ATIP infomation + /// + /// + /// + public AtipInfo(IntPtr buffer, int size) : base(buffer, size) + { + byte b; + + if (size < 4) + return; + + b = Get8(4); + IndicativeTargetWritingPower = (byte)((b >> 4) & 0x0f); + IsDDCD = GetBit(4, 3); + ReferenceSpeed = (byte)(b & 0x07); + + b = Get8(5); + UnrestrictedUse = GetBit(5, 6); + + b = Get8(6); + MediaCDRW = GetBit(6, 6); + MediaSubtype = (byte)((b >> 3) & 0x07); + + A1Valid = GetBit(6, 2); + A2Valid = GetBit(6, 1); + A3Valid = GetBit(6, 0); + + StartTimeOfLeadin = new MinuteSecondFrame(Get8(8), Get8(9), Get8(10)); + LastPossibleStartOfLeadout = new MinuteSecondFrame(Get8(12), Get8(13), Get8(14)); + + A1Values = new byte[3]; + for (int i = 0; i < 3; i++) + A1Values[i] = Get8(i + 16); + + A2Values = new byte[3]; + for (int i = 0; i < 3; i++) + A2Values[i] = Get8(i + 20); + + A3Values = new byte[3]; + for (int i = 0; i < 3; i++) + A3Values[i] = Get8(i + 24); + } + } +} diff --git a/Bwg.Scsi/Bwg.Scsi.csproj b/Bwg.Scsi/Bwg.Scsi.csproj new file mode 100644 index 0000000..789a3d1 --- /dev/null +++ b/Bwg.Scsi/Bwg.Scsi.csproj @@ -0,0 +1,159 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {A05B6AA6-0EC3-495D-BCC4-ECE1210071A8} + Library + Properties + Bwg.Scsi + Bwg.Scsi + + + + + + + + + + + 2.0 + + + v3.5 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + bin\Debug\Bwg.Scsi.XML + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + bin\Release\Bwg.Scsi.XML + + + true + bin\x64\Debug\ + DEBUG;TRACE + true + bin\Debug\Bwg.Scsi.XML + false + full + x64 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + + + bin\x64\Release\ + TRACE + true + bin\Release\Bwg.Scsi.XML + true + pdbonly + x64 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + + + true + bin\x86\Debug\ + DEBUG;TRACE + true + bin\Debug\Bwg.Scsi.XML + true + full + x86 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + + + bin\x86\Release\ + TRACE + true + bin\Release\Bwg.Scsi.XML + true + pdbonly + x86 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {F2DFEB00-BB35-4665-85EA-CB8C7729A6B7} + Bwg.Logging + + + + + \ No newline at end of file diff --git a/Bwg.Scsi/CapacityDescriptor.cs b/Bwg.Scsi/CapacityDescriptor.cs new file mode 100644 index 0000000..43fefad --- /dev/null +++ b/Bwg.Scsi/CapacityDescriptor.cs @@ -0,0 +1,109 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for DotNet +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Scsi +{ + /// + /// + /// + public class CapacityDescriptor : Result + { + #region public types + /// + /// + /// + public enum DescriptorType : byte + { + /// + /// + /// + Reserved = 0, + + /// + /// + /// + Unformatted = 1, + + /// + /// + /// + Formatted = 2, + + /// + /// + /// + NoMedia = 3 + } ; + #endregion + + #region public variables + + /// + /// The number of blocks on this media. + /// + public readonly uint NumberOfBlocks; + + /// + /// This length of a single block for this capacity + /// + public readonly uint BlockLength; + + /// + /// This is the descriptor type, see the SCSI-3 MMC specification + /// + public readonly DescriptorType DescType; + + /// + /// This is the type of format for the capacity descriptor, see the SCSI-3 MMC spec + /// + public readonly byte FormatType; + + #endregion + + #region constructor + /// + /// + /// + /// + /// + /// + public CapacityDescriptor(IntPtr buffer, int offset, int size) + : base(buffer, size) + { + byte b; + + NumberOfBlocks = Get32(offset); + + b = Get8(offset + 4); + DescType = (DescriptorType)(b & 0x03); + FormatType = (byte)(b >> 2); + + BlockLength = Get24(offset + 5); + } + #endregion + } +} diff --git a/Bwg.Scsi/Command.cs b/Bwg.Scsi/Command.cs new file mode 100644 index 0000000..27e5a03 --- /dev/null +++ b/Bwg.Scsi/Command.cs @@ -0,0 +1,236 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for DotNet +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; + +namespace Bwg.Scsi +{ + class Command : IDisposable + { + [DllImport("ntdll.dll")] + internal static extern void RtlZeroMemory(IntPtr dest, int size); + + public enum CmdDirection + { + In, + Out, + None + } ; + + /// + /// The data buffer for the SCSI command + /// + private int m_buffer_size; + private IntPtr m_buffer; + private bool m_delete_buffer; + + /// + /// The CDB buffer for the command. This gets copied into the SCSI_PASS_THROUGH_DIRECT + /// structure when this command is sent to the SCSI target. + /// + private byte[] m_cdb; + + /// + /// The direction of data transfer via the m_buffer field above + /// + private CmdDirection m_dir; + + /// + /// The amount of time to pass in seconds before a timeout + /// + private int m_timeout; + + public Command(ScsiCommandCode code, byte cdbsize, int bufsize, CmdDirection dir, int timeout) + { + Debug.Assert(bufsize < UInt16.MaxValue); + + m_cdb = new byte[cdbsize]; + m_cdb[0] = (byte)code; + + m_buffer_size = bufsize; + if (bufsize == 0) + m_buffer = IntPtr.Zero; + else + { + m_delete_buffer = true; + m_buffer = Marshal.AllocHGlobal(bufsize); + RtlZeroMemory(m_buffer, bufsize); + } + + m_dir = dir; + m_timeout = timeout; + } + + public Command(ScsiCommandCode code, byte cdbsize, IntPtr buffer, int bufsize, CmdDirection dir, int timeout) + { + m_cdb = new byte[cdbsize]; + m_cdb[0] = (byte)code; + + m_buffer_size = bufsize; + if (bufsize == 0) + m_buffer = IntPtr.Zero; + else + { + m_delete_buffer = false; + m_buffer = buffer; + } + + m_dir = dir; + m_timeout = timeout; + } + + public int TimeOut + { + get + { + return m_timeout; + } + } + + public ushort GetCDBLength() + { + return (ushort)m_cdb.GetLength(0); + } + + public CmdDirection Direction { get { return m_dir; } } + public int BufferSize { get { return m_buffer_size; } } + public IntPtr GetBuffer() { return m_buffer ; } + public byte GetCDB(byte addr) { return m_cdb[addr] ; } + + public void SetCDB8(byte addr, byte value) + { + m_cdb[addr] = value; + } + + public void SetCDB16(byte addr, ushort value) + { + m_cdb[addr] = (byte)((value >> 8) & 0xff); + m_cdb[addr + 1] = (byte)(value & 0xff); + } + + public void SetCDB24(byte addr, uint value) + { + m_cdb[addr] = (byte)((value >> 16) & 0xff); + m_cdb[addr + 1] = (byte)((value >> 8) & 0xff); + m_cdb[addr + 2] = (byte)(value & 0xff); + } + + public void SetCDB24(byte addr, int value) + { + m_cdb[addr] = (byte)((value >> 16) & 0xff); + m_cdb[addr + 1] = (byte)((value >> 8) & 0xff); + m_cdb[addr + 2] = (byte)(value & 0xff); + } + + public void SetCDB32(byte addr, uint value) + { + m_cdb[addr] = (byte)((value >> 24) & 0xff); + m_cdb[addr + 1] = (byte)((value >> 16) & 0xff); + m_cdb[addr + 2] = (byte)((value >> 8) & 0xff); + m_cdb[addr + 3] = (byte)(value & 0xff); + } + + public void SetCDB32(byte addr, int value) + { + m_cdb[addr] = (byte)((value >> 24) & 0xff); + m_cdb[addr + 1] = (byte)((value >> 16) & 0xff); + m_cdb[addr + 2] = (byte)((value >> 8) & 0xff); + m_cdb[addr + 3] = (byte)(value & 0xff); + } + + public void SetBuffer8(ushort addr, byte value) + { + Marshal.WriteByte(m_buffer, addr, value); + } + + public void SetBuffer16(ushort addr, ushort value) + { + Marshal.WriteByte(m_buffer, addr + 0, (byte)((value >> 8) & 0xff)); + Marshal.WriteByte(m_buffer, addr + 1, (byte)((value >> 0) & 0xff)); + } + + public void SetBuffer24(ushort addr, uint value) + { + Marshal.WriteByte(m_buffer, addr + 0, (byte)((value >> 16) & 0xff)); + Marshal.WriteByte(m_buffer, addr + 1, (byte)((value >> 8) & 0xff)); + Marshal.WriteByte(m_buffer, addr + 2, (byte)((value >> 0) & 0xff)); + } + + public void SetBuffer32(ushort addr, uint value) + { + Marshal.WriteByte(m_buffer, addr + 0, (byte)((value >> 24) & 0xff)); + Marshal.WriteByte(m_buffer, addr + 1, (byte)((value >> 16) & 0xff)); + Marshal.WriteByte(m_buffer, addr + 2, (byte)((value >> 8) & 0xff)); + Marshal.WriteByte(m_buffer, addr + 3, (byte)((value >> 0) & 0xff)); + } + + public byte GetBuffer8(ushort addr) + { + return Marshal.ReadByte(m_buffer, addr); + } + + public ushort GetBuffer16(ushort addr) + { + ushort v = 0; + + v |= (ushort)(Marshal.ReadByte(m_buffer, addr) << 8); + v |= (ushort)(Marshal.ReadByte(m_buffer, addr + 1) << 0); + + return v; + } + + public uint GetBuffer24(ushort addr) + { + uint v = 0; + + v |= (uint)(Marshal.ReadByte(m_buffer, addr) << 16); + v |= (uint)(Marshal.ReadByte(m_buffer, addr + 1) << 8); + v |= (uint)(Marshal.ReadByte(m_buffer, addr + 2) << 0); + + return v; + } + + public uint GetBuffer32(ushort addr) + { + uint v = 0; + + v |= (uint)(Marshal.ReadByte(m_buffer, addr + 0) << 24); + v |= (uint)(Marshal.ReadByte(m_buffer, addr + 1) << 16); + v |= (uint)(Marshal.ReadByte(m_buffer, addr + 2) << 8); + v |= (uint)(Marshal.ReadByte(m_buffer, addr + 3) << 0); + + return v; + } + + public void Dispose() + { + if (m_buffer_size > 0 && m_delete_buffer) + Marshal.FreeHGlobal(m_buffer); + m_cdb = null; + } + } +} diff --git a/Bwg.Scsi/Defs.cs b/Bwg.Scsi/Defs.cs new file mode 100644 index 0000000..3d3c96f --- /dev/null +++ b/Bwg.Scsi/Defs.cs @@ -0,0 +1,81 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for DotNet +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +namespace Bwg.Scsi +{ + enum ScsiCommandCode + { + TestUnitReady = 0x00, + RequestSense = 0x03, + FormatUnit = 0x04, + Inquiry = 0x12, + StartStopUnit = 0x1B, + PreventAllowMediumRemoval = 0x1E, + ReadFormatCapacities = 0x23, + ReadCapacity = 0x25, + Read = 0x28, + Seek = 0x2B, + Write = 0x2A, + Erase = 0x2C, + WriteAndVerify = 0x2E, + Verify = 0x2F, + SyncronizeCache = 0x35, + WriteBuffer = 0x3B, + ReadBuffer = 0x3C, + ReadSubChannel = 0x42, + ReadTocPmaAtip = 0x43, + PlayAudio10 = 0x45, + GetConfiguration = 0x46, + PlayAudioMSF = 0x47, + GetEventStatusNotification = 0x4A, + PauseResume = 0x4B, + StopPlayScan = 0x4E, + ReadDiskInformation = 0x51, + ReadTrackInformation = 0x52, + ReserveTrack = 0x53, + SendOpcInformation = 0x54, + ModeSelect = 0x55, + RepairTrack = 0x58, + ModeSense = 0x5A, + CloseTrackSession = 0x5B, + ReadBufferCapacity = 0x5C, + SendCueSheet = 0x5D, + Blank = 0xA1, + SendKey = 0xA3, + ReportKey = 0xA4, + PlayAudio12 = 0xA5, + LoadUnloadMedium = 0xA6, + SetReadAhead = 0xA7, + Read12 = 0xA8, + Write12 = 0xAA, + GetPerformance = 0xAC, + ReadDvdStructure = 0xAD, + SetStreaming = 0xB6, + ReadCdMSF = 0xB9, + Scan = 0xBA, + SetCdSpeed = 0xBB, + MechanismStatus = 0xBD, + ReadCd = 0xBE, + SendDvdStructure = 0xBF + } ; +} diff --git a/Bwg.Scsi/Device.cs b/Bwg.Scsi/Device.cs new file mode 100644 index 0000000..40e3b47 --- /dev/null +++ b/Bwg.Scsi/Device.cs @@ -0,0 +1,3686 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for DotNet +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime; +using System.Runtime.InteropServices; +using System.IO; +using System.Diagnostics; +using Bwg.Logging; + +namespace Bwg.Scsi +{ + // + // Functions not yet supported: + // MechanismStatus() + // ReadBuffer() + // ReportKey() + // WriteVerify() + // WriteBuffer() + + /// + /// + /// + public unsafe class Device : WinDev + { + #region Structures for DeviceIoControl + [StructLayout(LayoutKind.Explicit)] + struct SCSI_PASS_THROUGH_DIRECT32 + { + [FieldOffset(0)]public ushort Length; + [FieldOffset(2)]public byte ScsiStatus; + [FieldOffset(3)]public byte PathId; + [FieldOffset(4)]public byte TargetId; + [FieldOffset(5)]public byte Lun; + [FieldOffset(6)]public byte CdbLength; + [FieldOffset(7)]public byte SenseInfoLength; + [FieldOffset(8)]public byte DataIn; + [FieldOffset(12)]public uint DataTransferLength; + [FieldOffset(16)]public uint TimeOutValue; + [FieldOffset(20)]public IntPtr DataBuffer; + [FieldOffset(24)]public uint SenseInfoOffset; + [FieldOffset(28)]public fixed byte CdbData[16]; + [FieldOffset(48)]public fixed byte SenseInfo[32]; + } ; + + [StructLayout(LayoutKind.Explicit)] + struct SCSI_PASS_THROUGH_DIRECT64 + { + [FieldOffset(0)]public ushort Length; + [FieldOffset(2)]public byte ScsiStatus; + [FieldOffset(3)]public byte PathId; + [FieldOffset(4)]public byte TargetId; + [FieldOffset(5)]public byte Lun; + [FieldOffset(6)]public byte CdbLength; + [FieldOffset(7)]public byte SenseInfoLength; + [FieldOffset(8)]public byte DataIn; + [FieldOffset(12)]public uint DataTransferLength; + [FieldOffset(16)]public uint TimeOutValue; + [FieldOffset(24)]public IntPtr DataBuffer; + [FieldOffset(32)]public uint SenseInfoOffset; + [FieldOffset(36)]public fixed byte CdbData[16]; + [FieldOffset(56)]public fixed byte SenseInfo[32]; + } ; + + [StructLayout(LayoutKind.Explicit)] + struct IO_SCSI_CAPABILITIES + { + [FieldOffset(0)]public uint Length; + [FieldOffset(4)]public uint MaximumTransferLength; + [FieldOffset(8)]public uint MaximumPhysicalPages; + [FieldOffset(12)]public uint SupportedAsynchronousEvents; + [FieldOffset(16)]public uint AlignmentMask; + [FieldOffset(20)]public byte TaggedQueuing; + [FieldOffset(21)]public byte AdapterScansDown ; + [FieldOffset(22)]public byte AdapterUsesPio; + } ; + + #endregion + + #region Private data members + /// + /// The bitsize of the OS + /// + private byte m_ossize; + private bool m_ignore_long_write_error; + private byte[] m_sense_info; + private byte m_scsi_status; + private Logger m_logger; + private int m_MaximumTransferLength; + static private IDictionary m_asc_map; + #endregion + + #region private static data structures + static ushort m_scsi_request_size_32 = 44; + static ushort m_scsi_request_size_64 = 56; + #endregion + + #region public constants + /// + /// This constant is the device code for an MMC device, which is a CDROM/DVD/BD/HD DVD drive of + /// some type. + /// + public const int MMCDeviceType = 5; + #endregion + + #region Private constants + private const uint IOCTL_SCSI_PASS_THROUGH_DIRECT = 0x4d014; + private const uint IOCTL_SCSI_GET_CAPABILITIES = 0x41010; + + private const uint ERROR_NOT_SUPPORTED = 50 ; + #endregion + + #region Public Types + + /// + /// This value can be used in the call to SetCDSpeed() to set the read or + /// write speed to an optimal value. + /// + public const ushort OptimumSpeed = 0xffff; + + /// + /// The track number for the "invisible" track which is the next track to be + /// written on the device. + /// + public const byte InvisibleTrack = 0xff; + + /// + /// The notification class indicating the type of events of interest + /// + [Flags] + public enum NotificationClass : byte + { + /// + /// + /// + Reserved1 = (1 << 0), + + /// + /// See SCSI Spec + /// + OperationalChange = (1 << 1), + + /// + /// See SCSI Spec + /// + PowerManagement = (1 << 2), + + /// + /// See SCSI Spec + /// + ExternalRequest = (1 << 3), + + /// + /// See SCSI Spec + /// + Media = (1 << 4), + + /// + /// See SCSI Spec + /// + MultiHost = (1 << 5), + + /// + /// See SCSI Spec + /// + DeviceBusy = (1 << 6), + + /// + /// See SCSI Spec + /// + Reserved2 = (1 << 7) + } ; + + /// + /// The close sessions/track type + /// + public enum CloseTrackSessionType : byte + { + /// + /// Stop background format on DVD+RW + /// + StopBackGroundFormat = 0, + + /// + /// Close a logical track + /// + CloseTrack = 1, + + /// + /// Close a sessions (finalize a CD) + /// + CloseSession = 2, + + /// + /// Finalize a disk + /// + FinalizeDisk = 3, + + /// + /// Close a DVD+RW session with minimal radius + /// + CloseSessionMinimalRadius = 4, + + /// + /// Finalize a disk with minimul radius + /// + FinalizeDiskWithMinimalRadius = 5, + + /// + /// Finalize a disk in the most compatible way + /// + FinalizeDiskCompatible = 6 + } ; + + /// + /// The track/session number type + /// + public enum ReadTrackType + { + /// + /// Select the track containing the given logical block + /// + LBA = 0, + + /// + /// Select the track given by a track number + /// + TrackNumber = 1, + + /// + /// Select the first track of the given session number + /// + SessionNumber = 2, + + /// + /// Reserved, do not use + /// + Reserved = 3 + } ; + + /// + /// The sense key for the sense information + /// + public enum SenseKeyType + { + /// + /// No sense information returned + /// + NoSense = 0, + + /// + /// The last command completed sucessfully, with some recovery operation involved + /// + RecoveredError = 1 , + + /// + /// The device is not ready + /// + NotReady = 2, + + /// + /// An error in the media or an error in the data + /// + MediumError = 3, + + /// + /// A non-recoverable hardware error occurred + /// + HardwareError = 4, + + /// + /// The request sent was not valid + /// + IllegalRequest = 5, + + /// + /// Removable media was removed, or some other unit attention error + /// + UnitAttention = 6, + + /// + /// A command that reads or writes the media was performed on a protected block + /// + DataProtect = 7, + + /// + /// A write once media encountered a non-blank media + /// + BlankCheck = 8, + + /// + /// Vendor specific sense information + /// + VendorSpecific = 9 + } ; + + /// + /// Return value from ScsiDevice methods indicating the status of the command + /// + public enum CommandStatus + { + /// + /// The method is not yet supported by this class + /// + NotSupported, + + /// + /// The window IOCTL failed. The property LastError can be queried to get the Win32 error + /// associated with the failure. + /// + IoctlFailed, + + /// + /// The SCSI device failed the result. The sense information can be queried to determine more + /// about why the method failed. + /// + DeviceFailed, + + /// + /// The method was sucessful + /// + Success + } ; + + /// + /// Type of blanking operations that can be performed + /// + public enum BlankType + { + /// + /// Blank the entire disk, perfoming a full blanking operation + /// + FullDisk = 0x00, + + /// + /// Blank the disk, blanking only those blocks required to indicate the disk is blank + /// + MinimalDisk = 0x01, + + /// + /// Blank only the track given. Not valid for DVD-RW media + /// + Track = 0x02, + + /// + /// Unreserve a reserved track track + /// + UnreserveTrack = 0x03, + + /// + /// Blank the track tail + /// + TrackTail = 0x04, + + /// + /// Unclose the session allowing data to be appended to the session + /// + UncloseSession = 0x05, + + /// + /// Blank the last session + /// + Session = 0x06 + } ; + + /// + /// Indicates the type of configuration to retreive from the device + /// + public enum GetConfigType + { + /// + /// Return information about all features associated with the device + /// + AllFeatures = 0, + + /// + /// Return information about only those features marked as current + /// + CurrentFeatures = 1, + + /// + /// Return information about one specific feature + /// + OneFeature = 2, + + /// + /// This value is reserved. Using this value will cause an exception to be thrown. + /// + Reserved = 3 + } ; + + /// + /// The type of load/unload action to take + /// + public enum LoadUnload + { + /// + /// Abort any ongoing load or unload operation + /// + Abort = 0, + + /// + /// Reserved, do not use. Using this as an argument will result in a exception + /// + Reserved = 1, + + /// + /// Load the media from a specific slot, or from the tray + /// + Load = 2, + + /// + /// Unload the media from the device + /// + Unload = 3 // Unload the media + } ; + + /// + /// + /// + public enum PageControl + { + /// + /// Return the mode page parameters currently active in the device + /// + Current = 0, + + /// + /// Returns a mask indicating which parameters are changable + /// + Changeable = 1, + + /// + /// Return the default mode page parameters for the requested page + /// + Default = 2, + + /// + /// Returns the saved mode page parameters for the requested page + /// + Saved = 3 + } ; + + /// + /// + /// + public enum PauseResumeAction + { + /// + /// Pause the CD/DVD drive if it is playing + /// + Pause, + + /// + /// Resume the CD/DVD drive if it is paused + /// + Resume + } ; + + /// + /// This type indicates the state of the drive with respect to removing the media. + /// + public enum PreventAllow + { + /// + /// Allow the media to be removed from the device + /// + Allow = 0, + + /// + /// Prevent the media from being removed from the device + /// + Prevent = 1, + + /// + /// Allow the media to be removed from the device (not sure what presistent means) + /// + PresistentAllow = 2, + + /// + /// Prevent the media from being removed from the device (not sure what presistent means) + /// + PresistentPrevent = 3 + } ; + + /// + /// Direction to perform the scan operation + /// + public enum ScanDirection + { + /// + /// Scan in the forward direction + /// + Forward, + + /// + /// Scan in the reverse direction + /// + Reverse + } ; + + /// + /// The type of scan operation + /// + public enum ScanType + { + /// + /// Logical block address can + /// + LogicalBlockAddress = 0, + + /// + /// Time based scan + /// + Time = 1, + + /// + /// Track number scan + /// + TrackNumber = 2, + + /// + /// Reserved, will cause an excpetion if used + /// + Reserved = 3 + } ; + + /// + /// This type gives the rotational contorl value when setting the speed of the + /// CD player + /// + public enum RotationalControl + { + /// + /// CLV and non-pure CAV + /// + CLVandNonPureCav = 0, + + /// + /// Pure Cav + /// + PureCav = 1, + } ; + + /// + /// The power control value used when starting, stopping the drive + /// + public enum PowerControl + { + /// + /// Do not change the current power setting + /// + NoChange = 0, + + /// + /// Put player into idle state, reset the standby timer + /// + IdleState = 2, + + /// + /// Place the drive in the standby state + /// + StandbyState = 3, + + /// + /// Place the drive in the sleep state + /// + SleepState = 5 + } ; + + /// + /// The start/stop state of the drive + /// + public enum StartState + { + /// + /// Stop the disk + /// + StopDisk = 0, + + /// + /// Start the disk + /// + StartDisk = 1, + + /// + /// Eject the disk + /// + EjectDisk = 2, + + /// + /// Load the disk + /// + LoadDisk = 3 + } ; + + #endregion + + #region Public Properties + + /// + /// This property, if true, supresses any sense error messages about long writes + /// in progress. This is done because this error is a normal part of the burn + /// process + /// + public bool DontDisplayIgnoreLongWriteInProgress + { + get + { + return m_ignore_long_write_error; + } + set + { + m_ignore_long_write_error = value; + } + } + + /// + /// Returns TRUE if the sense information contains information about the + /// progress of the current operation + /// + public bool HasSenseProgressInformation + { + get + { + if (m_sense_info == null) + return false; + + return (m_sense_info[15] & 0x80) != 0; + } + } + + /// + /// The raw progress information from the sense information + /// + public ushort ProgressInformation + { + get + { + return (ushort)(m_sense_info[16] << 8 | m_sense_info[17]); + } + } + + /// + /// The maximum transfer length for this SCSI device on this SCSI channel + /// + public int MaximumTransferLength + { + get + { + return m_MaximumTransferLength; + } + } + + /// + /// This property returns true if the current sense error is a write protect error + /// + public bool IsWriteProtectError + { + get { return this.GetSenseAsc() == 0x27 && this.GetSenseAscq() == 0; } + } + + /// + /// If TRUE, log sense state + /// + public bool LogSenseState; + + #endregion + + #region Private Functions + + /// + /// Return a string associated with the ASC/ASCQ bytes + /// + /// the sense ASC value, see the SCSI spec + /// the sense ASCQ value, see the SCSI spec + /// a string representing the error codes given by ASC and ASCQ + public static string LookupSenseError(byte asc, byte ascq) + { + ushort data = Mix(asc, ascq) ; + if (m_asc_map.ContainsKey(data)) + return m_asc_map[data]; + + return "NO SENSE STRING FOR ASC=" + asc.ToString("X") + ", ASCQ=" + ascq.ToString("X"); + } + + /// + /// Send the sense information to the logger + /// + private void LogSenseInformation(Command cmd) + { + if (m_ignore_long_write_error && IsLongWriteInProgress(GetSenseAsc(), GetSenseAscq())) + return; + + if (m_logger != null && LogSenseState) + { + int len = GetSenseLength(); + int offset = 0; + int line = 0; + UserMessage m; + string str; + Logger logger = GetLogger(); + + str = "SCSI Operation Failed: " ; + str += LookupSenseError(GetSenseAsc(), GetSenseAscq()) ; + m = new UserMessage(UserMessage.Category.Error, 0, str); + m_logger.LogMessage(m); + + str = " SenseKey = " + GetSenseKey().ToString("X"); + m = new UserMessage(UserMessage.Category.Error, 0, str); + m_logger.LogMessage(m); + + str = " SenseAsc = " + GetSenseAsc().ToString("X"); + m = new UserMessage(UserMessage.Category.Error, 0, str); + m_logger.LogMessage(m); + + str = " SenseAscq = " + GetSenseAscq().ToString("X"); + m = new UserMessage(UserMessage.Category.Error, 0, str); + m_logger.LogMessage(m); + + while (offset < len) + { + line = 0; + str = " SenseData:"; + while (offset < len && line < 8) + { + str += " " + GetSenseByte(offset++).ToString("X2"); + line++; + } + + m = new UserMessage(UserMessage.Category.Error, 0, str); + logger.LogMessage(m); + } + + if (GetSenseAsc() == 0x24 && GetSenseAscq() == 0x00) + { + // This is invalid field in CDB, so dump the CDB to the log + str = "INVALID CDB:"; + for (byte i = 0; i < cmd.GetCDBLength(); i++) + { + byte b = cmd.GetCDB(i); + str += " " + b.ToString("X"); + } + m = new UserMessage(UserMessage.Category.Error, 0, str); + m_logger.LogMessage(m); + } + } + } + + private static ushort Mix(byte asc, byte ascq) + { + return (ushort)((asc << 8) | ascq); + } + + #region Initialize ASC/ASCQ Error table + private static void InitAscTable() + { + m_asc_map = new Dictionary(); + + m_asc_map[Mix(0x00, 0x00)] = "NO ADDITIONAL SENSE INFORMATION"; + m_asc_map[Mix(0x00, 0x06)] = "I/O PROCESS TERMINATED"; + + m_asc_map[Mix(0x01, 0x00)] = "NO INDEX/SECTOR SIGNAL"; + m_asc_map[Mix(0x01, 0x02)] = "NO SEEK COMPLETE"; + m_asc_map[Mix(0x01, 0x03)] = "PERIPHERAL DEVICE WRITE FAULT"; + + m_asc_map[Mix(0x04, 0x00)] = "LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE"; + m_asc_map[Mix(0x04, 0x01)] = "LOGICAL UNIT IS IN PROCESS OF BECOMING READY"; + m_asc_map[Mix(0x04, 0x02)] = "LOGICAL UNIT NOT READY, INITIALIZING COMMAND REQUIRED"; + m_asc_map[Mix(0x04, 0x03)] = "LOGICAL UNIT NOT READY, MANUAL INTERVENTION REQUIRED"; + m_asc_map[Mix(0x04, 0x04)] = "LOGICAL UNIT NOT READY, FORMAT IN PROGRESS"; + m_asc_map[Mix(0x04, 0x07)] = "LOGICAL UNIT NOT READY, OPERATION IN PROGRESS"; + m_asc_map[Mix(0x04, 0x08)] = "LOGICAL UNIT NOT READY, LONG WRITE IN PROGRESS"; + + m_asc_map[Mix(0x05, 0x00)] = "LOGICAL UNIT DOES NOT RESPOND TO SELECTION"; + + m_asc_map[Mix(0x08, 0x00)] = "LOGICAL UNIT COMMUNICATION FAILURE"; + m_asc_map[Mix(0x08, 0x01)] = "LOGICAL UNIT COMMUNICATION TIME-OUT"; + m_asc_map[Mix(0x08, 0x02)] = "LOGICAL UNIT COMMUNICATION PARITY ERROR"; + m_asc_map[Mix(0x08, 0x03)] = "LOGICAL UNIT COMMUNICATION CRC ERROR (ULTRA-DMA/32)"; + m_asc_map[Mix(0x08, 0x04)] = "UNREACHABLE COPY TARGET"; + + m_asc_map[Mix(0x0c, 0x00)] = "WRITE ERROR"; + + m_asc_map[Mix(0x10, 0x00)] = "ID CRC OR ECC ERROR"; + + m_asc_map[Mix(0x11, 0x00)] = "UNRECOVERED READ ERROR"; + m_asc_map[Mix(0x11, 0x01)] = "READ RETRIES EXHAUSTED"; + m_asc_map[Mix(0x11, 0x02)] = "ERROR TOO LONG TO CORRECT"; + m_asc_map[Mix(0x11, 0x05)] = "L-EC UNCORRECTABLE ERROR"; + m_asc_map[Mix(0x11, 0x06)] = "CIRC UNRECOVERED ERROR"; + m_asc_map[Mix(0x11, 0x0F)] = "ERROR READING UPC/EAN NUMBER"; + m_asc_map[Mix(0x11, 0x10)] = "ERROR READING ISRC NUMBER"; + m_asc_map[Mix(0x11, 0x11)] = "READ ERROR – LOSS OF STREAMING"; + + m_asc_map[Mix(0x20, 0x00)] = "INVALID COMMAND OPERATION CODE"; + + m_asc_map[Mix(0x21, 0x00)] = "LOGICAL BLOCK ADDRESS OUT OF RANGE"; + m_asc_map[Mix(0x21, 0x01)] = "INVALID ELEMENT ADDRESS"; + m_asc_map[Mix(0x21, 0x02)] = "INVALID ADDRESS FOR WRITE"; + + m_asc_map[Mix(0x24, 0x00)] = "INVALID FIELD IN CDB"; + + m_asc_map[Mix(0x26, 0x00)] = "INVALID FIELD IN PARAMETER LIST"; + m_asc_map[Mix(0x26, 0x01)] = "PARAMETER NOT SUPPORTED"; + m_asc_map[Mix(0x26, 0x02)] = "PARAMETER VALUE INVALID"; + m_asc_map[Mix(0x26, 0x03)] = "THRESHOLD PARAMETERS NOT SUPPORTED"; + + m_asc_map[Mix(0x27, 0x00)] = "WRITE PROTECTED"; + + m_asc_map[Mix(0x28, 0x00)] = "NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED"; + m_asc_map[Mix(0x28, 0x01)] = "IMPORT OR EXPORT ELEMENT ACCESSED"; + + m_asc_map[Mix(0x29, 0x00)] = "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED"; + m_asc_map[Mix(0x29, 0x01)] = "POWER ON OCCURRED"; + m_asc_map[Mix(0x29, 0x02)] = "BUS RESET OCCURRED"; + m_asc_map[Mix(0x29, 0x03)] = "BUS DEVICE RESET FUNCTION OCCURRED"; + m_asc_map[Mix(0x29, 0x04)] = "DEVICE INTERNAL RESET"; + + m_asc_map[Mix(0x2A, 0x00)] = "PARAMETERS CHANGED"; + m_asc_map[Mix(0x2A, 0x01)] = "MODE PARAMETERS CHANGED"; + m_asc_map[Mix(0x2A, 0x02)] = "LOG PARAMETERS CHANGED"; + + m_asc_map[Mix(0x2C, 0x00)] = "COMMMAND SEQUENCE ERROR"; + m_asc_map[Mix(0x2C, 0x03)] = "CURRENT PROGRAM AREA IS NOT EMPTY"; + m_asc_map[Mix(0x2C, 0x04)] = "CURRENT PROGRAM AREA IS EMPTY"; + + m_asc_map[Mix(0x2E, 0x00)] = "INSUFFICIENT TIME FOR OPERATION"; + + m_asc_map[Mix(0x30, 0x00)] = "INCOMPATIBLE MEDIUM INSTALLED"; + m_asc_map[Mix(0x30, 0x01)] = "CANNOT READ MEDIUM - UNKNOWN FORMAT"; + m_asc_map[Mix(0x30, 0x02)] = "CANNOT READ MEDIUM - INCOMPATIBLE FORMAT"; + m_asc_map[Mix(0x30, 0x03)] = "CLEANING CARTRIDGE INSTALLED"; + m_asc_map[Mix(0x30, 0x04)] = "CANNOT WRITE MEDIUM - UNKONWN FORMAT"; + m_asc_map[Mix(0x30, 0x05)] = "CANNOT WRITE MEDIUM - INCOMPATIBLE FORMAT"; + m_asc_map[Mix(0x30, 0x06)] = "CANNOT FORMAT MEDIUM - INCOMPATIBLE FORMAT"; + m_asc_map[Mix(0x30, 0x07)] = "CLEANING FAILURE"; + m_asc_map[Mix(0x30, 0x08)] = "CANNOT WRITE - APPLICATION CODE MISMATCH"; + m_asc_map[Mix(0x30, 0x09)] = "CURRENT SESSION NOT FIXATED FOR APPEND"; + m_asc_map[Mix(0x30, 0x10)] = "MEDIUM NOT FORMATTED"; + m_asc_map[Mix(0x30, 0x11)] = "CANNOT WRITE MEDIUM - UNSUPPORTED MEDIUM VERSION"; + + m_asc_map[Mix(0x3A, 0x00)] = "MEDIUM NOT PRESENT"; + m_asc_map[Mix(0x3A, 0x01)] = "MEDIUM NOT PRESENT - TRAY CLOSED"; + m_asc_map[Mix(0x3A, 0x02)] = "MEDIUM NOT PRESENT - TRAY OPEN"; + + m_asc_map[Mix(0x3B, 0x0D)] = "MEDIUM DESTINATION ELEMENT FULL"; + m_asc_map[Mix(0x3B, 0x0E)] = "MEDIUM SOURCE ELEMENT EMPTY"; + m_asc_map[Mix(0x3B, 0x0F)] = "END OF MEDIUM REACHED"; + m_asc_map[Mix(0x3B, 0x11)] = "MEDIUM MAGAZINE NOT ACCESSIBLE"; + m_asc_map[Mix(0x3B, 0x12)] = "MEDIUM MAGAZINE REMOVED"; + m_asc_map[Mix(0x3B, 0x13)] = "MEDIUM MAGAZINE INSERTED"; + m_asc_map[Mix(0x3B, 0x14)] = "MEDIUM MAGAZINE LOCKED"; + m_asc_map[Mix(0x3B, 0x15)] = "MEDIUM MAGAZINE UNLOCKED"; + + m_asc_map[Mix(0x3E, 0x00)] = "LOGICAL UNIT HAS NOT SELF-CONFIGURED YET"; + m_asc_map[Mix(0x3E, 0x01)] = "LOGICAL UNIT FAILURE"; + m_asc_map[Mix(0x3E, 0x02)] = "TIMEOUT ON LOGICAL UNIT"; + + m_asc_map[Mix(0x3F, 0x00)] = "TARGET OPERATING CONDITIONS HAVE CHANGED"; + m_asc_map[Mix(0x3F, 0x01)] = "MICROCODE HAS BEEN CHANGED"; + m_asc_map[Mix(0x3F, 0x02)] = "CHANGED OPERATING DEFINITION"; + m_asc_map[Mix(0x3F, 0x03)] = "INQUIRY DATA HAS CHANGED"; + + m_asc_map[Mix(0x53, 0x00)] = "MEDIA LOAD OR EJECT FAILED"; + m_asc_map[Mix(0x53, 0x02)] = "MEDIUM REMOVAL PREVENTED"; + + m_asc_map[Mix(0x57, 0x00)] = "UNABLE TO RECOVER TABLE OF CONTENTS"; + + m_asc_map[Mix(0x5A, 0x00)] = "OPERATOR REQUEST OR STATE CHANGE INPUT"; + m_asc_map[Mix(0x5A, 0x01)] = "OPERATOR MEDIUM REMOVAL REQUEST"; + m_asc_map[Mix(0x5A, 0x02)] = "OPERATOR SELECTED WRITE PROTECT"; + m_asc_map[Mix(0x5A, 0x03)] = "OPERATOR SELECTED WRITE PERMIT"; + + m_asc_map[Mix(0x5B, 0x00)] = "LOG EXCEPTION"; + m_asc_map[Mix(0x5B, 0x01)] = "THRESHOLD CONDITION MET"; + m_asc_map[Mix(0x5B, 0x02)] = "LOG COUNTER AT MAXIMUM"; + m_asc_map[Mix(0x5B, 0x03)] = "LOG LIST CODES EXHAUSTED"; + + m_asc_map[Mix(0x5E, 0x00)] = "LOW POWER CONDITION ON"; + m_asc_map[Mix(0x5E, 0x01)] = "IDLE CONDITION ACTIVATED BY TIMER"; + m_asc_map[Mix(0x5E, 0x02)] = "STANDBY CONDITION ACTIVATED BY TIMER"; + m_asc_map[Mix(0x5E, 0x03)] = "IDLE CONDITION ACTIVATED BY COMMAND"; + m_asc_map[Mix(0x5E, 0x04)] = "STANDBY CONDITION ACTIVATED BY COMMAND"; + + m_asc_map[Mix(0x63, 0x00)] = "END OF USER AREA ENCOUNTERED ON THIS TRACK"; + m_asc_map[Mix(0x63, 0x01)] = "PACKET DOES NOT FIT IN AVAILABLE SPACE"; + + m_asc_map[Mix(0x64, 0x00)] = "ILLEGAL MODE FOR THIS TRACK"; + m_asc_map[Mix(0x64, 0x01)] = "INVALID PACKET SIZE"; + + m_asc_map[Mix(0x65, 0x00)] = "VOLTAGE FAULT"; + + m_asc_map[Mix(0x6F, 0x00)] = "COPY PROTECTION KEY EXCHANGE FAILURE - AUTHENTICATION FAILURE"; + m_asc_map[Mix(0x6F, 0x01)] = "COPY PROTECTION KEY EXCHANGE FAILURE - KEY NOT PRESENT"; + m_asc_map[Mix(0x6F, 0x02)] = "COPY PROTECTION KEY EXCHANGE FAILURE - KEY NOT ESTABLISHED"; + m_asc_map[Mix(0x6F, 0x03)] = "READ OF SCRAMBLED SECTOR WITHOUT AUTHENTICATION"; + m_asc_map[Mix(0x6F, 0x04)] = "MEDIA REGION CODE IS MISMATCHED TO LOGICAL UNIT REGION"; + m_asc_map[Mix(0x6F, 0x05)] = "LOGICAL UNIT REGION MUST BE PERMANENT/REGION RESET COUNT ERROR"; + + m_asc_map[Mix(0x72, 0x00)] = "SESSION FIXATION ERROR"; + m_asc_map[Mix(0x72, 0x01)] = "SESSION FIXATION ERROR WRITING LEAD-IN"; + m_asc_map[Mix(0x72, 0x02)] = "SESSION FIXATION ERROR WRITING LEAD-OUT"; + m_asc_map[Mix(0x72, 0x03)] = "SESSION FIXATION ERROR - INCOMPLETE TRACK IN SESSION"; + m_asc_map[Mix(0x72, 0x04)] = "EMPTY OR PARTIALLY WRITTEN RESERVED TRACK"; + m_asc_map[Mix(0x72, 0x05)] = "NO MORE TRACK RESERVATIONS ALLOWED"; + m_asc_map[Mix(0x72, 0x06)] = "RMZ EXTENSION IS NOT ALLOWED"; + m_asc_map[Mix(0x72, 0x07)] = "NO MORE TEST ZONE EXTENSIONS ARE ALLOWED"; + + m_asc_map[Mix(0x73, 0x00)] = "CD CONTROL ERROR"; + m_asc_map[Mix(0x73, 0x01)] = "POWER CALIBRATION AREA ALMOST"; + m_asc_map[Mix(0x73, 0x02)] = "POWER CALIBRATION AREA IS FULL"; + m_asc_map[Mix(0x73, 0x03)] = "POWER CALIBRATION AREA ERROR"; + m_asc_map[Mix(0x73, 0x04)] = "PROGRAM MEMORY AREA UPDATE FAILURE"; + m_asc_map[Mix(0x73, 0x05)] = "PROGRAM MEMORY AREA IS FULL"; + m_asc_map[Mix(0x73, 0x06)] = "RMA/PMA IS ALMOST FULL"; + } + #endregion + + private CommandStatus SendCommand(Command cmd) + { + return (m_ossize == 32) ? SendCommand32(cmd) : SendCommand64(cmd); + } + + private CommandStatus SendCommand64(Command cmd) + { + SCSI_PASS_THROUGH_DIRECT64 f = new SCSI_PASS_THROUGH_DIRECT64(); + f.Length = m_scsi_request_size_64; + f.CdbLength = (byte)cmd.GetCDBLength(); + f.DataIn = 0; + if (cmd.Direction == Command.CmdDirection.In) + f.DataIn = 1; + f.DataTransferLength = (uint)cmd.BufferSize; + f.TimeOutValue = (uint)cmd.TimeOut; + for (byte i = 0; i < f.CdbLength; i++) + f.CdbData[i] = cmd.GetCDB(i); + f.SenseInfoOffset = 56; + f.SenseInfoLength = 24; + + uint total = (uint)Marshal.SizeOf(f); + + uint ret = 0; + + // Set the buffer field + f.DataBuffer = cmd.GetBuffer(); + + // Send through ioctl field + IntPtr pt = new IntPtr(&f); + if (!Control(IOCTL_SCSI_PASS_THROUGH_DIRECT, pt, total, pt, total, ref ret, IntPtr.Zero)) + { + string str ; + str = "IOCTL_SCSI_PASS_THROUGH_DIRECT failed - " + Win32ErrorToString(LastError); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Error, 0, str)); + return CommandStatus.IoctlFailed; + } + + m_scsi_status = f.ScsiStatus; + if (f.SenseInfoLength != 0) + { + m_sense_info = new byte[f.SenseInfoLength]; + for (int i = 0; i < f.SenseInfoLength; i++) + m_sense_info[i] = f.SenseInfo[i]; + } + else + m_sense_info = null; + + if (m_scsi_status != 0) + { + LogSenseInformation(cmd); + return CommandStatus.DeviceFailed; + } + + return CommandStatus.Success; + } + + private CommandStatus SendCommand32(Command cmd) + { + SCSI_PASS_THROUGH_DIRECT32 f = new SCSI_PASS_THROUGH_DIRECT32(); + f.Length = m_scsi_request_size_32; + f.CdbLength = (byte)cmd.GetCDBLength(); + f.DataIn = 0; + if (cmd.Direction == Command.CmdDirection.In) + f.DataIn = 1; + f.DataTransferLength = (uint)cmd.BufferSize; + f.TimeOutValue = (uint)cmd.TimeOut; + for (byte i = 0; i < f.CdbLength; i++) + f.CdbData[i] = cmd.GetCDB(i); + f.SenseInfoOffset = 48; + f.SenseInfoLength = 24; + + uint total = (uint)Marshal.SizeOf(f); + + uint ret = 0; + + // Set the buffer field + f.DataBuffer = cmd.GetBuffer(); + + // Send through ioctl field + IntPtr pt = new IntPtr(&f); + if (!Control(IOCTL_SCSI_PASS_THROUGH_DIRECT, pt, total, pt, total, ref ret, IntPtr.Zero)) + { + string str ; + str = "IOCTL_SCSI_PASS_THROUGH_DIRECT failed - " + Win32ErrorToString(LastError); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Error, 0, str)); + return CommandStatus.IoctlFailed; + } + + m_scsi_status = f.ScsiStatus; + if (f.SenseInfoLength != 0) + { + m_sense_info = new byte[f.SenseInfoLength]; + for (int i = 0; i < f.SenseInfoLength; i++) + m_sense_info[i] = f.SenseInfo[i]; + } + else + m_sense_info = null; + + if (m_scsi_status != 0) + { + LogSenseInformation(cmd); + return CommandStatus.DeviceFailed; + } + + return CommandStatus.Success; + } + + void QueryBufferSize() + { + IO_SCSI_CAPABILITIES f = new IO_SCSI_CAPABILITIES(); + f.Length = 23; + + uint ret = 0; + IntPtr pt = new IntPtr(&f); + uint total = (uint)Marshal.SizeOf(f); + if (Control(IOCTL_SCSI_GET_CAPABILITIES, pt, total, pt, total, ref ret, IntPtr.Zero)) + { + if (f.MaximumTransferLength > Int32.MaxValue) + m_MaximumTransferLength = Int32.MaxValue; + else + m_MaximumTransferLength = (int)f.MaximumTransferLength; + + if (m_MaximumTransferLength > 56 * 2048) + m_MaximumTransferLength = 56 * 2048; + } + else + { + // + // This is hardcoded for now because it works, until I can figure out + // how to query devices for the maximum amount of data that can be transferred + // at one time. + // + m_MaximumTransferLength = 26 * 2048; + } + } + + #endregion + + #region constructor + /// + /// + /// + public Device(Logger l) + { + m_logger = l; + LogSenseState = true; + InitAscTable(); + + IntPtr p = new IntPtr(); + if (Marshal.SizeOf(p) == 4) + m_ossize = 32; + else + m_ossize = 64; + + // m_logger.LogMessage(new UserMessage(UserMessage.Category.Info, 0, "Operating System Size: " + m_ossize.ToString())); + } + + static Device() + { + InitAscTable(); + } + + #endregion + + #region Public Functions + + /// + /// This static method returns true if the error given by the ASC and ASCQ values + /// indicate a long write in progress + /// + /// + /// + /// + public static bool IsLongWriteInProgress(byte asc, byte ascq) + { + return asc == 0x04 && ascq == 0x08 ; + } + + /// + /// Open the SCSI device + /// + /// name of the SCSI device + /// true if the open succeeded + public override bool Open(string name) + { + if (!base.Open(name)) + return false; + + QueryBufferSize(); + + return true; + } + + /// + /// + /// + /// + /// + public override bool Open(char letter) + { + if (!base.Open(letter)) + return false ; + + QueryBufferSize(); + + return true; + } + + /// + /// This method return TRUE if the current SENSE error is the one given by the + /// two parameters to the function + /// + /// + /// + /// + public bool IsScsiError(byte asc, byte ascq) + { + return GetSenseAsc() == asc && GetSenseAscq() == ascq; + } + + /// + /// This function resets the device. + /// + /// true, if the reset was sucessful, false otherwise + public bool Reset() + { + // + // How do I reset a driver that is speaking pass through SCSI? It might be + // an IDE drive, SCSI drive, USB drive, or Fireware drive. + // + return true; + } + + /// + /// Get the logger from the device + /// + /// The logger object + public Logger GetLogger() + { + return m_logger; + } + + /// + /// + /// + /// + public int GetSenseLength() + { + if (m_sense_info == null) + return 0; + + return m_sense_info.GetLength(0); + } + + /// + /// + /// + /// + /// + public byte GetSenseByte(int addr) + { + return m_sense_info[addr]; + } + + /// + /// + /// + /// + public byte GetScsiStatus() + { + return m_scsi_status; + } + + /// + /// Get the ASC byte from the sense information + /// + /// asc byte + public byte GetSenseAsc() + { + return m_sense_info[12]; + } + + /// + /// Get the ASCQ byte from the sense information + /// + /// ascq byte + public byte GetSenseAscq() + { + return m_sense_info[13]; + } + + /// + /// Get the 32 bits of sense information (offset 3) + /// + /// sense information + public uint GetSenseInformation() + { + return (uint)((m_sense_info[3] << 24) | (m_sense_info[4] << 16) | (m_sense_info[5] << 8) | m_sense_info[6]); + } + + /// + /// Get the 32 bits of command specific information (offset 8) + /// + /// command specific sense information + public uint GetSenseCommandSpecificInformation() + { + return (uint)((m_sense_info[8] << 24) | (m_sense_info[9] << 16) | (m_sense_info[10] << 8) | m_sense_info[11]); + } + + /// + /// This method returns the sense key for the sense information + /// + /// The SenseKeyType value that indicates the sense key for the sense information + public SenseKeyType GetSenseKey() + { + return (SenseKeyType)(m_sense_info[2] & 0x0f); + } + + + #endregion + + #region SCSI Commands + + /// + /// This function blanks a portion of the CD or DVD that is in the drive. + /// + /// If true, this function returns immediately and the blanking + /// happens in the backgroun. If false, this funtion does not return until the blanking operation + /// is complete + /// The type of blanking operation + /// The address for the blanking operation if an address is required + /// + /// Success - the command complete sucessfully + /// IoctlFailed - the windows DeviceIoControl failed, LastError give the Win32 error code + /// DeviceFailed - the device failed the command, the sense information has more data + /// + public CommandStatus Blank(bool immd, BlankType t, int addr) + { + if (m_logger != null) + { + string args = immd.ToString() + ", " + t.ToString() + ", " + addr.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.Blank(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.Blank, 12, 0, Command.CmdDirection.None, 60*30)) + { + byte b = (byte)t; + if (immd) + b |= (1 << 4); + cmd.SetCDB8(1, b); + + cmd.SetCDB32(2, addr); + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// This command allows closure of either a track or a session + /// + /// If true, return immediately and perform the function in the background + /// This parameter indicates the close type, see the SCSI-3 MMC spec for more information + /// For those close types requiring a track number, this is the number, see the SCSI-3 MMC spec for more information + /// + public CommandStatus CloseTrackSession(bool immd, CloseTrackSessionType closetype, ushort track) + { + if (m_logger != null) + { + string args = immd.ToString() + ", 0x" + closetype.ToString() + ", " + track.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.CloseTrackSession(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.CloseTrackSession, 10, 0, Command.CmdDirection.None, 60 * 5)) + { + if (immd) + cmd.SetCDB8(1, 1); + + cmd.SetCDB8(2, (byte)closetype); + cmd.SetCDB16(4, track) ; + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + return CommandStatus.Success; + } + + /// + /// This function instructs the device to erase blocks on the disk. + /// + /// If true, return immediately and perform the function in the background + /// The starting block address for the erase operation + /// The number of blocks to erase. If this count is zero, the ERA bit is set in the SCSI erase request + /// + public CommandStatus Erase(bool immd, uint lba, ushort count) + { + if (m_logger != null) + { + string args = immd.ToString() + ", " + lba.ToString() + ", " + count.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.Erase(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.Erase, 10, 0, Command.CmdDirection.None, 60 * 30)) + { + byte b = 0; + + if (immd) + b |= 0x02; + if (count == 0) + b |= 0x04; + cmd.SetCDB8(1, b); + cmd.SetCDB32(2, lba); + cmd.SetCDB16(7, count); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// This method sends the format unit command down to the SCSI device. + /// + /// + public CommandStatus FormatUnit(FormatParameterList plist) + { + if (m_logger != null) + { + string args = "plist" ; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.FormatUnit(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.FormatUnit, 6, (ushort)plist.Size, Command.CmdDirection.Out, 60*60)) + { + cmd.SetCDB8(1, 0x11); + + plist.FormatToMemory(cmd.GetBuffer()); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + /// + /// + /// + public CommandStatus GetConfiguration(GetConfigType type, ushort start, out FeatureList result) + { + if (m_logger != null) + { + string args = type.ToString() + ", " + start.ToString() + ", out result"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.GetConfiguration(" + args + ")")); + } + + uint len = 0; + result = null; + + if (type == GetConfigType.Reserved) + throw new Exception("cannot use reserved value") ; + + using (Command cmd = new Command(ScsiCommandCode.GetConfiguration, 10, 8, Command.CmdDirection.In, 10)) + { + cmd.SetCDB8(1, (byte)type); + cmd.SetCDB16(2, start); + cmd.SetCDB16(7, 8); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + len = cmd.GetBuffer32(0); + len += 4; // Add four for the length field + } + + using (Command cmd = new Command(ScsiCommandCode.GetConfiguration, 10, (ushort)len, Command.CmdDirection.In, 10)) + { + cmd.SetCDB8(1, (byte)type); + cmd.SetCDB16(2, start); + cmd.SetCDB16(7, (ushort)len); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + result = new FeatureList(cmd.GetBuffer(), cmd.BufferSize); + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + /// + /// + /// + public CommandStatus GetEventStatusNotification(bool polled, NotificationClass request, out EventStatusNotification result) + { + if (m_logger != null) + { + string args = polled.ToString() + ", " + request.ToString() + ", out result"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.GetEventStatusNotification(" + args + ")")); + } + + ushort len = 0; + result = null; + + using (Command cmd = new Command(ScsiCommandCode.GetEventStatusNotification, 10, 4, Command.CmdDirection.In, 10)) + { + if (polled) + cmd.SetCDB8(1, 1); + cmd.SetCDB8(4, (byte)request); + cmd.SetCDB16(7, 4); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + byte n = cmd.GetBuffer8(2); + if ((n & 0x80) != 0) + { + // There are no events, just capture the header + result = new EventStatusNotification(cmd.GetBuffer(), cmd.BufferSize); + return CommandStatus.Success; + } + + // + // There are event notifications to be grabbed, allocate space for these + // + len = cmd.GetBuffer16(0); + len += 4; // For the length field + } + + using (Command cmd = new Command(ScsiCommandCode.GetEventStatusNotification, 10, len, Command.CmdDirection.In, 10)) + { + if (polled) + cmd.SetCDB8(1, 1); + cmd.SetCDB8(4, (byte)request); + cmd.SetCDB16(7, len); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + result = new EventStatusNotification(cmd.GetBuffer(), cmd.BufferSize); + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus GetSpeed(out SpeedDescriptorList list) + { + ushort initial_size = 8 + 4 * 16; + list = null; + + if (m_logger != null) + { + string args = string.Empty; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.GetWriteSpeed(" + args + ")")); + } + + uint len = 0; + using (Command cmd = new Command(ScsiCommandCode.GetPerformance, 12, initial_size, Command.CmdDirection.In, 10 * 60)) + { + cmd.SetCDB16(8, (ushort)((initial_size - 8) / 16)); + cmd.SetCDB8(10, 3); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + len = cmd.GetBuffer32(0); + len += 4; // For the length field + } + + using (Command cmd = new Command(ScsiCommandCode.GetPerformance, 12, (ushort)len, Command.CmdDirection.In, 10 * 60)) + { + cmd.SetCDB16(8, (ushort)((len - 8) / 16)); + cmd.SetCDB8(10, 3); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + + list = new SpeedDescriptorList(cmd.GetBuffer(), cmd.BufferSize); + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus GetPerformance(uint lba, PerformanceList.DataType rwtype, PerformanceList.ExceptType extype, out PerformanceList list) + { + list = null; + + if (m_logger != null) + { + string args = rwtype.ToString() + ", " + extype.ToString() + ", list"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.GetPerformance(" + args + ")")); + } + + uint len = 0; + using (Command cmd = new Command(ScsiCommandCode.GetPerformance, 12, 24, Command.CmdDirection.In, 10)) + { + byte b = 0x10; + if (rwtype == PerformanceList.DataType.WriteData) + b |= 0x04 ; + + b |= (byte)extype; + + cmd.SetCDB8(1, b); + cmd.SetCDB16(8, 1); + + if (extype == PerformanceList.ExceptType.Entire) + cmd.SetCDB32(2, lba); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + len = cmd.GetBuffer32(0); + len += 4; // For the length field + } + + using (Command cmd = new Command(ScsiCommandCode.GetPerformance, 12, (ushort)len, Command.CmdDirection.In, 10)) + { + byte b = 0x10; + if (rwtype == PerformanceList.DataType.WriteData) + b |= 0x04; + + b |= (byte)extype; + + cmd.SetCDB8(1, b); + + if (extype == PerformanceList.ExceptType.Entire) + cmd.SetCDB32(2, lba); + + cmd.SetCDB16(8, (ushort)((len - 8) / 16)); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + list = new PerformanceList(cmd.GetBuffer(), cmd.BufferSize); + } + + return CommandStatus.Success; + } + + /// + /// Perform a SCSI inquiry on the device to get information about the device + /// + /// The return value describing the inquiry results + /// + public CommandStatus Inquiry(out InquiryResult result) + { + if (m_logger != null) + { + string args = "out result"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.Inquiry(" + args + ")")); + } + + result = null; + + byte len = 0; + using (Command cmd = new Command(ScsiCommandCode.Inquiry, 6, 36, Command.CmdDirection.In, 10)) + { + cmd.SetCDB16(3, 36); + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + len = cmd.GetBuffer8(4); + len += 4; + + result = new InquiryResult(cmd.GetBuffer(), cmd.BufferSize); + + // + // As an oddity, the Sony DW-G120A only supports requests that are an even number + // of bytes. + // + if ((len % 2) == 1) + len = (byte)((len / 2 * 2) + (((len % 2) == 1) ? 2 : 0)); + } + + using (Command cmd = new Command(ScsiCommandCode.Inquiry, 6, len, Command.CmdDirection.In, 100)) + { + cmd.SetCDB8(4, len); + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + result = new InquiryResult(cmd.GetBuffer(), cmd.BufferSize); + + if (m_logger != null) + m_logger.DumpBuffer(9, "Raw Inquiry Result", cmd.GetBuffer(), cmd.BufferSize); + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + /// + /// + /// + public CommandStatus LoadUnloadMedium(bool immd, LoadUnload action, byte slot) + { + if (m_logger != null) + { + string args = immd.ToString() + ", " + action.ToString() + ", " + slot.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.LoadUnloadMedium(" + args + ")")); + } + + if (action == LoadUnload.Reserved) + throw new Exception("invalid action - cannot use reserved value"); + + using (Command cmd = new Command(ScsiCommandCode.LoadUnloadMedium, 12, 0, Command.CmdDirection.None, 60 * 5)) + { + if (immd) + cmd.SetCDB8(1, 1); + + cmd.SetCDB8(4, (byte)action); + cmd.SetCDB8(8, slot); + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus MechanismStatus() + { + if (m_logger != null) + { + string args = "NOT IMPLEMENTED"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.MechanismStatus(" + args + ")")); + } + return CommandStatus.NotSupported; + } + + /// + /// + /// + /// + /// + /// + public CommandStatus ModeSelect(bool save, ModeTable table) + { + if (m_logger != null) + { + string t = ""; + for (int i = 0; i < table.Pages.Count; i++) + { + if (t.Length > 0) + t += ", "; + t = t + table.Pages[i].PageCode; + } + string args = save.ToString() + ", ModeTable(" + t + ")"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ModeSelect(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.ModeSelect, 10, table.Size, Command.CmdDirection.Out, 60 * 5)) + { + byte b = 0x10; + if (save) + b |= 0x01; + + cmd.SetCDB8(1, b); + cmd.SetCDB16(7, table.Size); + table.Format(cmd.GetBuffer()); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + /// + /// + /// + public CommandStatus ModeSense(PageControl pc, byte page, out ModeTable table) + { + if (m_logger != null) + { + string args = pc.ToString() + ", " + page.ToString() + ", out result"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ModeSense(" + args + ")")); + } + + ushort len = 0; + + table = null; + + byte b = 0; + b |= (byte)((byte)(pc) << 7); + b |= (byte)(page & 0x3f); + + using (Command cmd = new Command(ScsiCommandCode.ModeSense, 10, 8, Command.CmdDirection.In, 60 * 5)) + { + cmd.SetCDB8(1, 8); // Disable block descriptors + cmd.SetCDB8(2, b); + cmd.SetCDB16(7, 8); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + len = cmd.GetBuffer16(0); + len += 2; + } + + using (Command cmd = new Command(ScsiCommandCode.ModeSense, 10, len, Command.CmdDirection.In, 60 * 5)) + { + cmd.SetCDB8(1, 8); // Disable block descriptors + cmd.SetCDB8(2, b); + cmd.SetCDB16(7, len); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + table = new ModeTable(cmd.GetBuffer(), cmd.BufferSize); + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + /// + public CommandStatus PauseResume(PauseResumeAction action) + { + if (m_logger != null) + { + string args = action.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.PauseResumeAction(" + args + ")")); + } + using (Command cmd = new Command(ScsiCommandCode.PauseResume, 10, 0, Command.CmdDirection.None, 60 * 5)) + { + if (action == PauseResumeAction.Resume) + cmd.SetCDB8(8, 1); + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// This command requests that the SCSI device begins an audio playback operation. + /// + /// The starting logical block address for the playback + /// The length of the disk to play + /// + public CommandStatus PlayAudio(uint lba, uint length) + { + if (m_logger != null) + { + string args = lba.ToString() + ", " + length.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.PlayAudio(" + args + ")")); + } + if (length > 65535) + { + using (Command cmd = new Command(ScsiCommandCode.PlayAudio12, 12, 0, Command.CmdDirection.None, 5)) + { + cmd.SetCDB32(2, lba); + cmd.SetCDB32(6, length); + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + } + else + { + using (Command cmd = new Command(ScsiCommandCode.PlayAudio10, 10, 0, Command.CmdDirection.None, 5)) + { + cmd.SetCDB32(2, lba); + cmd.SetCDB16(7, (ushort)length); + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + /// + /// + public CommandStatus PlayAudio(MinuteSecondFrame start, MinuteSecondFrame end) + { + if (m_logger != null) + { + string args = "NOT IMPLEMENTED"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.PlayAudio(" + args + ")")); + } + return CommandStatus.NotSupported; + } + + /// + /// + /// + /// + /// + public CommandStatus PreventAllowMediumRemoval(PreventAllow action) + { + if (m_logger != null) + { + string args = action.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.PreventAllowMediumRemoval(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.PreventAllowMediumRemoval, 6, 0, Command.CmdDirection.None, 60 * 5)) + { + cmd.SetCDB8(4, (byte)action); + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// This method requests that the SCSI device transfer data to the given buffer + /// + /// If true, the data is forced from the media and cannot be read from the cache + /// If true, this is a streaming read + /// The starting logical address for the data + /// The length of the data to read + /// The data buffer to received the data + /// + public CommandStatus Read(bool force, bool streaming, uint lba, uint length, ref byte [] data) + { + if (m_logger != null) + { + string args = force.ToString() + ", " + streaming.ToString() + ", " + lba.ToString() + ", " + length.ToString() + ", buffer"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.Read(" + args + ")")); + } + + if (streaming || length > 65535) + { + using (Command cmd = new Command(ScsiCommandCode.Read12, 12, (ushort)data.GetLength(0), Command.CmdDirection.In, 5 * 60)) + { + if (force) + cmd.SetCDB8(1, 4); // Set the FUA bit + + cmd.SetCDB32(2, lba); + cmd.SetCDB32(6, length); + + if (streaming) + cmd.SetCDB8(10, 0x80); // Set the streaming bit + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + Marshal.Copy(cmd.GetBuffer(), data, 0, data.GetLength(0)); + } + } + else + { + using (Command cmd = new Command(ScsiCommandCode.Read, 10, (ushort)data.GetLength(0), Command.CmdDirection.In, 5 * 60)) + { + if (force) + cmd.SetCDB8(1, 4); // Set the FUA bit + + cmd.SetCDB32(2, lba); + cmd.SetCDB16(7, (ushort)length); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + Marshal.Copy(cmd.GetBuffer(), data, 0, data.GetLength(0)); + } + } + + return CommandStatus.Success; + } + + /// + /// Read data from the device into a memory buffer + /// + /// If true, the data is forced from the media and cannot be read from the cache + /// If true, this is a streaming read + /// The starting logical address for the data + /// The length of the data to read + /// The buffer to receive the data + /// The size of the buffer given by the data parameter + /// + public CommandStatus Read(bool force, bool streaming, uint lba, uint length, IntPtr data, ushort size) + { + if (m_logger != null) + { + string args = force.ToString() + ", " + streaming.ToString() + ", " + lba.ToString() + ", " + length.ToString() + ", IntPtr, " + size.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.Read(" + args + ")")); + } + + if (streaming || length > 65535) + { + using (Command cmd = new Command(ScsiCommandCode.Read12, 12, data, size, Command.CmdDirection.In, 5 * 60)) + { + if (force) + cmd.SetCDB8(1, 4); + + cmd.SetCDB32(2, lba); + cmd.SetCDB32(6, length); + + if (streaming) + cmd.SetCDB8(10, 0x80); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + } + else + { + using (Command cmd = new Command(ScsiCommandCode.Read, 10, data, size, Command.CmdDirection.In, 5 * 60)) + { + if (force) + cmd.SetCDB8(1, 4); // Set the FUA bit + + cmd.SetCDB32(2, lba); + cmd.SetCDB16(7, (ushort)length); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus ReadBuffer() + { + if (m_logger != null) + { + string args = "NOT IMPLEMENTED"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadBuffer(" + args + ")")); + } + return CommandStatus.NotSupported; + } + + /// + /// This method returns the buffer capacity for the pass through SCSI device. + /// + /// if true, the length and available is given in blocks, otherwise bytes + /// return vaue, the length of the buffer + /// return value, the available space in the buffer + /// status of the command + public CommandStatus ReadBufferCapacity(bool blocks, out int length, out int avail) + { + if (m_logger != null) + { + string args = blocks.ToString() + ", out length, out available"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 9, "Bwg.Scsi.Device.ReadBufferCapacity(" + args + ")")); + } + length = 0; + avail = 0; + + using (Command cmd = new Command(ScsiCommandCode.ReadBufferCapacity, 10, 12, Command.CmdDirection.In, 60 * 5)) + { + cmd.SetCDB16(7, 12); + if (blocks) + cmd.SetCDB8(1, 1); + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + length = (int)cmd.GetBuffer32(4); + avail = (int)cmd.GetBuffer32(8); + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + /// + /// + public CommandStatus ReadCapacity(out uint lba, out uint blocklen) + { + if (m_logger != null) + { + string args = "out lba, out blocklen"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadCapacity(" + args + ")")); + } + blocklen = 0; + lba = 0; + + using (Command cmd = new Command(ScsiCommandCode.ReadCapacity, 10, 12, Command.CmdDirection.In, 60 * 5)) + { + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + lba = cmd.GetBuffer32(0); + blocklen = cmd.GetBuffer32(4); + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + /// + /// + /// + /// the memory area + /// the size of the memory area given by the data parameter + /// + public CommandStatus ReadCD(byte exp, bool dap, uint start, uint length, IntPtr data, int size) + { + if (m_logger != null) + { + string args = exp.ToString() + ", " + dap.ToString() + ", " + start.ToString() + ", " + length.ToString() + ", data, " + size.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadCD(" + args + ")")); + } + + if (exp != 1 && exp != 2 && exp != 3 && exp != 4 && exp != 5) + return CommandStatus.NotSupported; + + using (Command cmd = new Command(ScsiCommandCode.ReadCd, 12, data, size, Command.CmdDirection.In, 5 * 60)) + { + byte b = (byte)((exp & 0x07) << 2); + if (dap) + b |= 0x02; + cmd.SetCDB8(1, b); + cmd.SetCDB32(2, start); + cmd.SetCDB24(6, length); + cmd.SetCDB8(9, 0x10); // User data only + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// subchannel mode + /// + /// + /// + /// + /// the memory area + /// the size of the memory area given by the data parameter + /// + public CommandStatus ReadCDAndSubChannel(byte mode, byte exp, bool dap, uint start, uint length, IntPtr data, int size) + { + if (m_logger != null) + { + string args = exp.ToString() + ", " + dap.ToString() + ", " + start.ToString() + ", " + length.ToString() + ", data, " + size.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadCD(" + args + ")")); + } + + if (mode != 1 && mode != 2 && mode != 4) + throw new Exception("invalid read mode for ReadSubchannel() call"); + + if (exp != 1 && exp != 2 && exp != 3 && exp != 4 && exp != 5) + return CommandStatus.NotSupported; + + using (Command cmd = new Command(ScsiCommandCode.ReadCd, 12, data, size, Command.CmdDirection.In, 5 * 60)) + { + byte b = (byte)((exp & 0x07) << 2); + if (dap) + b |= 0x02; + cmd.SetCDB8(1, b); + cmd.SetCDB32(2, start); + cmd.SetCDB24(6, length); + cmd.SetCDB8(9, 0x10); // User data only + cmd.SetCDB8(10, mode); // Subchannel + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// Read CD header data from the disk + /// + /// the sector number containing the header data to read + /// the return data read + /// + public CommandStatus ReadCD(uint sector, out HeaderData hdr) + { + hdr = null; + + if (m_logger != null) + { + string args = sector.ToString() + ", out HeaderData hdr" ; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadCD(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.ReadCd, 12, 4, Command.CmdDirection.In, 10)) + { + cmd.SetCDB32(2, sector); + cmd.SetCDB24(6, 1); + cmd.SetCDB8(9, 0x20); // Header data only + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + hdr = new HeaderData(cmd.GetBuffer(), cmd.BufferSize); + } + + return CommandStatus.Success; + } + + /// + /// This method reads subheader CD data from a data mode 2 track + /// + /// the sector # of the data to read + /// return subheader data + /// + public CommandStatus ReadCD(uint sector, out SubheaderData hdr) + { + hdr = null ; + if (m_logger != null) + { + string args = sector.ToString() + ", out SubheaderData hdr"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadCD(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.ReadCd, 12, 4, Command.CmdDirection.In, 10)) + { + cmd.SetCDB32(2, sector); + cmd.SetCDB24(6, 1); + cmd.SetCDB8(9, 0x40); // Header data only + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + hdr = new SubheaderData(cmd.GetBuffer(), cmd.BufferSize); + } + + return CommandStatus.Success; + } + + /// + /// Read the subchannel data from a series of sectors + /// + /// + /// + /// + /// the subchannel mode + /// + public CommandStatus ReadSubChannel(byte mode, uint sector, uint length, ref byte[] data) + { + byte bytes_per_sector; + + if (m_logger != null) + { + string args = sector.ToString() + ", " + length.ToString() + ", data"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadSubChannel(" + args + ")")); + } + + if (mode != 1 && mode != 2 && mode != 4) + throw new Exception("invalid read mode for ReadSubchannel() call"); + + bytes_per_sector = 96; + if (mode == 2) + bytes_per_sector = 16; + + if (data.GetLength(0) < length * bytes_per_sector) + throw new Exception("data buffer is not large enough to hold the data requested"); + + + using (Command cmd = new Command(ScsiCommandCode.ReadCd, 12, (int)(length * bytes_per_sector), Command.CmdDirection.In, 10 * 60)) + { + cmd.SetCDB32(2, sector); // The sector number to start with + cmd.SetCDB24(6, length); // The length in sectors + cmd.SetCDB8(10, mode); // Corrected, de-interleaved P - W data + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + Marshal.Copy(cmd.GetBuffer(), data, 0, (int)(length * bytes_per_sector)); + } + return CommandStatus.Success ; + } + + /// + /// Read the CD text information from the leadin using the ReadTocPmaAtip command form + /// + /// + /// + public CommandStatus ReadCDText(out byte [] data) + { + ushort len; + + if (m_logger != null) + { + string args = "info"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadCDText(" + args + ")")); + } + + data = null; + using (Command cmd = new Command(ScsiCommandCode.ReadTocPmaAtip, 10, 4, Command.CmdDirection.In, 5 * 60)) + { + cmd.SetCDB8(2, 5); // CDText info in leadin + cmd.SetCDB16(7, 4); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + len = cmd.GetBuffer16(0); + len += 2; + + if (len <= 4) + { + data = new byte[len]; + Marshal.Copy(cmd.GetBuffer(), data, 0, len); + return CommandStatus.Success; + } + } + + using (Command cmd = new Command(ScsiCommandCode.ReadTocPmaAtip, 10, len, Command.CmdDirection.In, 5 * 60)) + { + cmd.SetCDB8(2, 5); // CDText info in leadin + cmd.SetCDB16(7, len); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + data = new byte[len]; + Marshal.Copy(cmd.GetBuffer(), data, 0, len); + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus ReadDiskInformation(out DiskInformation result) + { + if (m_logger != null) + { + string args = "out result"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadDiskInformation(" + args + ")")); + } + + ushort len = 0; + result = null; + + using (Command cmd = new Command(ScsiCommandCode.ReadDiskInformation, 10, 34, Command.CmdDirection.In, 60)) + { + cmd.SetCDB16(7, 34); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + + len = cmd.GetBuffer16(0); + if (len <= 34) + { + result = new DiskInformation(cmd.GetBuffer(), cmd.BufferSize); + return CommandStatus.Success; + } + len += 2; + } + + using (Command cmd = new Command(ScsiCommandCode.ReadDiskInformation, 10, len, Command.CmdDirection.In, 60)) + { + cmd.SetCDB16(7, len); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + result = new DiskInformation(cmd.GetBuffer(), cmd.BufferSize); + } + + return CommandStatus.Success; + } + + /// + /// Return the AACS Volume Identifier as a series of bytes + /// + /// the AACS volume identifier + /// status of command execution + public CommandStatus ReadDiskStructureAACSVolumeIdentifier(out byte [] data) + { + return ReadDiskStructureReturnBytes(0x80, out data); + } + + /// + /// Return the AACS media serial number + /// + /// the media serial number + /// status of command execution + public CommandStatus ReadDiskStructureAACSMediaSerialNumber(out byte[] data) + { + return ReadDiskStructureReturnBytes(0x81, out data); + } + + /// + /// This method reads the PAC data for a blu-ray disk + /// + /// the pac id of the pac to read + /// the format of the pac to read + /// return storage for the pac data + /// + public CommandStatus ReadDiskStructurePac(uint pacid, byte pacfmt, out byte [] data) + { + data = null; + + if (m_logger != null) + { + string args = "out byte[] data"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadDvdStructurePac(" + args + ")")); + } + + ushort len = 4; + using (Command cmd = new Command(ScsiCommandCode.ReadDvdStructure, 12, len, Command.CmdDirection.In, 60)) + { + cmd.SetCDB24(2, pacid); + cmd.SetCDB8(6, pacfmt); + cmd.SetCDB8(7, 0x30); // Read PAC data + cmd.SetCDB16(8, len); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + len = (ushort)(cmd.GetBuffer16(0) + 2); + if (len == 2) + { + data = new byte[0]; + return CommandStatus.Success; + } + Debug.Assert(len < 8192); + } + + using (Command cmd = new Command(ScsiCommandCode.ReadDvdStructure, 12, len, Command.CmdDirection.In, 60)) + { + cmd.SetCDB24(2, pacid); + cmd.SetCDB8(6, pacfmt); + cmd.SetCDB8(7, 0x30); // Read manufacturing information + cmd.SetCDB16(8, len); // Up to 2k of data + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + len = (ushort)(cmd.GetBuffer16(0) + 2); + data = new byte[len]; + Marshal.Copy(cmd.GetBuffer(), data, 0, len); + } + + return CommandStatus.Success; + } + + /// + /// Return the data from a ReadDvdStructure request as a series of bytes. + /// + /// the format of the read dvd structure request + /// the AACS volume identifier + /// status of command execution + private CommandStatus ReadDiskStructureReturnBytes(byte format, out byte[] data) + { + data = null; + + if (m_logger != null) + { + string args = "out byte[] data"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadDvdStructureAACSVolumeIdentifier(" + args + ")")); + } + + ushort len = 4; + using (Command cmd = new Command(ScsiCommandCode.ReadDvdStructure, 12, len, Command.CmdDirection.In, 60)) + { + cmd.SetCDB8(7, format); // Read manufacturing information + cmd.SetCDB16(8, len); // Up to 2k of data + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + len = cmd.GetBuffer16(0); + if (len == 0) + { + data = new byte[0]; + return CommandStatus.Success; + } + Debug.Assert(len < 8192); + } + + using (Command cmd = new Command(ScsiCommandCode.ReadDvdStructure, 12, len, Command.CmdDirection.In, 60)) + { + cmd.SetCDB8(7, format); // Read manufacturing information + cmd.SetCDB16(8, len); // Up to 2k of data + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + len = cmd.GetBuffer16(0); + data = new byte[len]; + Marshal.Copy(cmd.GetBuffer(), data, 0, len); + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus ReadDiskStructure(uint addr, byte layer, byte format, ref byte [] data) + { + if (m_logger != null) + { + string args = string.Empty; + args += "addr=" + addr.ToString(); + args += ", layer=" + layer.ToString(); + args += ", format=" + format.ToString(); + args += "ref byte[] data"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadDvdStructure(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.ReadDvdStructure, 12, 2048, Command.CmdDirection.In, 60)) + { + cmd.SetCDB32(2, addr) ; // Address = 0 + cmd.SetCDB8(6, layer); // Layer number = 0 + cmd.SetCDB8(7, format); // Read manufacturing information + cmd.SetCDB16(8, 2048); // Up to 2k of data + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + int len = data.GetLength(0); + if (len > 2048) + len = 2048; + Marshal.Copy(cmd.GetBuffer(), data, 0, len); + } + + return CommandStatus.Success; + } + + /// + /// Query the media for the spare sectors available + /// + /// the number of primary spare sectors available + /// the number of secondary spare sectors available + /// the number of secondary spare sectors total + /// + public CommandStatus ReadDVDRamSpareAreaInfo(out uint primary, out uint sec_avail, out uint sec_total) + { + primary = 0; + sec_avail = 0; + sec_total = 0; + + if (m_logger != null) + { + string args = "out primary, out sec_avail, out sec_total"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadDVDRamSpareAreaInfo(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.ReadDvdStructure, 12, 2048, Command.CmdDirection.In, 60)) + { + cmd.SetCDB8(7, 0x0a); // Read manufacturing information + cmd.SetCDB16(8, 16); // Up to 2k of data + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + primary = cmd.GetBuffer32(4); + sec_avail = cmd.GetBuffer32(8); + sec_total = cmd.GetBuffer32(12); + } + + return CommandStatus.Success; + } + + /// + /// Returns the size of layer 0 for DL media (DVD+R DL and DVD-R DL) + /// + /// return value, if true size of L0 is changable + /// return value, the current size of L0 + /// status of the command + public CommandStatus ReadDvdLayer0Size(out bool isfixed, out uint size) + { + if (m_logger != null) + { + string args = "ReadDvdLayer0Size, out bool isfixed, out uint size"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadDvdStructure(" + args + ")")); + } + + size = 0; + isfixed = false; + + using (Command cmd = new Command(ScsiCommandCode.ReadDvdStructure, 12, 12, Command.CmdDirection.In, 5 * 60)) + { + cmd.SetCDB8(7, 0x20); // Read manufacturing information + cmd.SetCDB8(8, 12); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + if ((cmd.GetBuffer8(4) & 0x80) != 0) + isfixed = true; + + size = cmd.GetBuffer32(8); + } + + return CommandStatus.Success; + } + + /// + /// Returns the start address of the Layer 0 middle zone (DVD+R DL, DVD-R DL) + /// + /// return value, if true, the middle zone start is changeable + /// return value, the location of the L0 middle zone + /// status of the command + public CommandStatus ReadDvdMiddleZoneStartAddr(out bool isfixed, out uint location) + { + if (m_logger != null) + { + string args = "ReadDvdMiddleZoneStartAddr, out bool isfixed, out uint size"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadDvdStructure(" + args + ")")); + } + + location = 0; + isfixed = false; + + using (Command cmd = new Command(ScsiCommandCode.ReadDvdStructure, 12, 12, Command.CmdDirection.In, 5 * 60)) + { + cmd.SetCDB8(7, 0x21); + cmd.SetCDB8(8, 12); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + if ((cmd.GetBuffer8(4) & 0x80) != 0) + isfixed = true; + + location = cmd.GetBuffer32(8); + } + + return CommandStatus.Success; + } + + /// + /// Reads the jump interval size (DVD-R DL) + /// + /// return value, the size of the jump interval + /// status of the command + public CommandStatus ReadDvdJumpIntervalSize(out uint size) + { + if (m_logger != null) + { + string args = "ReadDvdJumpIntervalSize, out uint size"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadDvdStructure(" + args + ")")); + } + size = 0; + + using (Command cmd = new Command(ScsiCommandCode.ReadDvdStructure, 12, 12, Command.CmdDirection.In, 5 * 60)) + { + cmd.SetCDB8(7, 0x22); + cmd.SetCDB8(8, 12); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + size = cmd.GetBuffer32(8); + } + + return CommandStatus.Success; + } + + /// + /// Returs the manual jump address for the media (DVD-R DL) + /// + /// return value, the jump address + /// the command status + public CommandStatus ReadDvdManualLayerJumpAddress(out uint addr) + { + if (m_logger != null) + { + string args = "ReadDvdManualLayerJumpAddress, out uint size"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadDvdStructure(" + args + ")")); + } + addr = 0; + + using (Command cmd = new Command(ScsiCommandCode.ReadDvdStructure, 12, 12, Command.CmdDirection.In, 5 * 60)) + { + cmd.SetCDB8(7, 0x23); + cmd.SetCDB8(8, 12); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + addr = cmd.GetBuffer32(8); + } + + return CommandStatus.Success; + } + + /// + /// This method returns the remapping address (DVD-R DL( + /// + /// the remapping address + /// the command status + public CommandStatus ReadDvdRemappingAddress(out uint location) + { + if (m_logger != null) + { + string args = "ReadDvdRemappingAddress, out uint size"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadDvdStructure(" + args + ")")); + } + + location = 0; + + using (Command cmd = new Command(ScsiCommandCode.ReadDvdStructure, 12, 12, Command.CmdDirection.In, 5 * 60)) + { + cmd.SetCDB8(7, 0x24); + cmd.SetCDB8(8, 12); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + location = cmd.GetBuffer32(8); + } + + return CommandStatus.Success; + } + + /// + /// Send the layer boundary information to the drive. + /// + /// the location of the boundary between layers in blocks + /// the status of the command + public CommandStatus SendDvdLayerBoundaryInformation(uint boundary) + { + if (m_logger != null) + { + string args = "SendDvdLayerBoundaryInformation, boundary=" + boundary.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.SendDvdStructure(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.SendDvdStructure, 12, 12, Command.CmdDirection.Out, 5 * 60)) + { + cmd.SetCDB8(7, 0x20); + cmd.SetCDB16(8, 12); + + cmd.SetBuffer16(0, 10); + cmd.SetBuffer32(8, boundary); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus ReadFormatCapacities(bool all, out IList caplist) + { + caplist = new List(); + + if (m_logger != null) + { + string args = all.ToString() + ", out caplist"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadFormatCapacities(" + args + ")")); + } + int len; + + using (Command cmd = new Command(ScsiCommandCode.ReadFormatCapacities, 10, 8, Command.CmdDirection.In, 60)) + { + cmd.SetCDB16(7, 8); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + len = cmd.GetBuffer8(3) + 4; + } + using (Command cmd = new Command(ScsiCommandCode.ReadFormatCapacities, 10, len, Command.CmdDirection.In, 60)) + { + cmd.SetCDB16(7, (ushort)len); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + int offset = 4; + len = cmd.GetBuffer8(3) + 4; + while (offset < len) + { + caplist.Add(new CapacityDescriptor(cmd.GetBuffer(), offset, cmd.BufferSize)); + offset += 8; + } + } + return CommandStatus.Success; + } + + /// + /// Read the table of contents from the disk + /// + /// the track or session to find the TOC for + /// a list return value containins a list of table of content entryies + /// time versus lba mode + /// + public CommandStatus ReadToc(byte track, bool mode, out IList toc) + { + ushort len; + toc = null; + + if (m_logger != null) + { + string args = "info"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadToc(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.ReadTocPmaAtip, 10, 16, Command.CmdDirection.In, 5 * 60)) + { + if (mode) + cmd.SetCDB8(1, 2); + cmd.SetCDB8(6, track); + cmd.SetCDB16(7, 16); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + len = cmd.GetBuffer16(0); + len += 2; + } + + using (Command cmd = new Command(ScsiCommandCode.ReadTocPmaAtip, 10, len, Command.CmdDirection.In, 5 * 60)) + { + if (mode) + cmd.SetCDB8(1, 2); + cmd.SetCDB8(6, track); + cmd.SetCDB16(7, len); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + int offset = 4; + toc = new List(); + + while (offset + 8 <= len) + { + TocEntry entry = new TocEntry(cmd.GetBuffer(), offset, cmd.BufferSize, mode); + toc.Add(entry); + + offset += 8; + } + } + + return CommandStatus.Success; + } + + /// + /// This method reads the ATIP information from a CD media + /// + /// + public CommandStatus ReadAtip(out AtipInfo info) + { + ushort len; + info = null; + + if (m_logger != null) + { + string args = "info"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadAtip(" + args + ")")); + } + using (Command cmd = new Command(ScsiCommandCode.ReadTocPmaAtip, 10, 32, Command.CmdDirection.In, 5 * 60)) + { + cmd.SetCDB8(2, 4); + cmd.SetCDB16(7, 32); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + len = cmd.GetBuffer16(0); + len += 2; + } + + if (len <= 4) + return CommandStatus.Success; + + using (Command cmd = new Command(ScsiCommandCode.ReadTocPmaAtip, 10, len, Command.CmdDirection.In, 5 * 60)) + { + cmd.SetCDB8(2, 4); + cmd.SetCDB16(7, len); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + info = new AtipInfo(cmd.GetBuffer(), len); + } + + return CommandStatus.Success; + } + + /// + /// This method reads the ATIP information from a CD media + /// + /// + public CommandStatus ReadFullToc(out byte[] data) + { + ushort len; + data = null; + + if (m_logger != null) + { + string args = "info"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadFullToc(" + args + ")")); + } + using (Command cmd = new Command(ScsiCommandCode.ReadTocPmaAtip, 10, 32, Command.CmdDirection.In, 5 * 60)) + { + cmd.SetCDB8(2, 2); + cmd.SetCDB16(7, 32); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + len = cmd.GetBuffer16(0); + len += 2; + + if (len <= 32) + { + data = new byte[len]; + Marshal.Copy(cmd.GetBuffer(), data, 0, len); + return CommandStatus.Success; + } + } + + using (Command cmd = new Command(ScsiCommandCode.ReadTocPmaAtip, 10, len, Command.CmdDirection.In, 5 * 60)) + { + cmd.SetCDB8(2, 2); + cmd.SetCDB16(7, len); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + //info = new AtipInfo(cmd.GetBuffer(), len); + data = new byte[len]; + Marshal.Copy(cmd.GetBuffer(), data, 0, len); + } + + return CommandStatus.Success; + } + + /// + /// This method reads the ATIP information from a CD media + /// + /// + public CommandStatus ReadPMA(out byte[] data) + { + ushort len; + data = null; + + if (m_logger != null) + { + string args = "info"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadFullToc(" + args + ")")); + } + using (Command cmd = new Command(ScsiCommandCode.ReadTocPmaAtip, 10, 32, Command.CmdDirection.In, 5 * 60)) + { + cmd.SetCDB8(2, 3); + cmd.SetCDB16(7, 32); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + len = cmd.GetBuffer16(0); + len += 2; + + if (len <= 32) + { + data = new byte[len]; + Marshal.Copy(cmd.GetBuffer(), data, 0, len); + return CommandStatus.Success; + } + } + + using (Command cmd = new Command(ScsiCommandCode.ReadTocPmaAtip, 10, len, Command.CmdDirection.In, 5 * 60)) + { + cmd.SetCDB8(2, 3); + cmd.SetCDB16(7, len); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + //info = new AtipInfo(cmd.GetBuffer(), len); + data = new byte[len]; + Marshal.Copy(cmd.GetBuffer(), data, 0, len); + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus ReadTrackInformation(ReadTrackType type, uint addr, out TrackInformation info) + { + info = null; + + if (m_logger != null) + { + string args = "info"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReadTrackInformation(" + args + ")")); + } + using (Command cmd = new Command(ScsiCommandCode.ReadTrackInformation, 10, 48, Command.CmdDirection.In, 5 * 60)) + { + cmd.SetCDB8(1, (byte)type); + cmd.SetCDB32(2, addr); + cmd.SetCDB16(7, 40); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + info = new TrackInformation(cmd.GetBuffer(), cmd.BufferSize); + } + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus RepairTrack(ushort track) + { + if (m_logger != null) + { + string args = track.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.RepairTrack(" + args + ")")); + } + using (Command cmd = new Command(ScsiCommandCode.RepairTrack, 10, 0, Command.CmdDirection.None, 5 * 60)) + { + cmd.SetCDB16(4, track); + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus ReportKey() + { + if (m_logger != null) + { + string args = "NOT IMPLEMENTED"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReportKey(" + args + ")")); + } + return CommandStatus.NotSupported; + } + + /// + /// + /// + /// + public CommandStatus RequestSense() + { + if (m_logger != null) + { + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.RequestSense()")); + } + + using (Command cmd = new Command(ScsiCommandCode.RequestSense, 6, 18, Command.CmdDirection.None, 5*60)) + { + cmd.SetCDB8(4, 18); + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + + m_sense_info = new byte[18]; + Marshal.Copy(cmd.GetBuffer(), m_sense_info, 0, 18); + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus ReserveTrack(uint size) + { + if (m_logger != null) + { + string args = size.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.ReserveTrack(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.ReserveTrack, 10, 0, Command.CmdDirection.None, 5 * 60)) + { + cmd.SetCDB32(5, size); + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus Scan(ScanDirection dir, uint lba, ScanType type) + { + if (m_logger != null) + { + string args = dir.ToString() + ", " + lba.ToString() + ", " + type.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.Scan(" + args + ")")); + } + + if (type == ScanType.Reserved) + throw new Exception("type parameter is of type Reserved"); + + using (Command cmd = new Command(ScsiCommandCode.Scan, 12, 0, Command.CmdDirection.None, 10)) + { + if (dir == ScanDirection.Reverse) + cmd.SetCDB8(1, 0x10); + cmd.SetCDB32(2, lba); + cmd.SetCDB8(9, (byte)((byte)type << 6)); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus Seek(uint lba) + { + if (m_logger != null) + { + string args = lba.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.Seek(" + args + ")")); + } + using (Command cmd = new Command(ScsiCommandCode.Seek, 10, 0, Command.CmdDirection.None, 10)) + { + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus SendCueSheet(byte [] sheet) + { + if (m_logger != null) + { + string args = sheet.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.SendCueSheet(" + args + ")")); + + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 1, "Cue Sheet")); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 1, "----------------------------------------------")); + for (int i = 0; i < sheet.GetLength(0) / 8; i++) + { + string s = "" ; + + for (int j = 0; j < 8; j++) + { + if (j != 0) + s += " "; + s += sheet[i * 8 + j].ToString("x2"); + } + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 1, s)); + } + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 1, "----------------------------------------------")); + } + + ushort len = (ushort)sheet.GetLength(0); + using (Command cmd = new Command(ScsiCommandCode.SendCueSheet, 10, len, Command.CmdDirection.Out, 10)) + { + cmd.SetCDB32(5, len); + cmd.SetCDB8(5, 0); + Marshal.Copy(sheet, 0, cmd.GetBuffer(), len); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus SendDVDStructure() + { + if (m_logger != null) + { + string args = "NOT IMPLEMENTED"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.SendDVDStructure(" + args + ")")); + } + return CommandStatus.NotSupported; + } + + /// + /// + /// + /// + public CommandStatus SendKey() + { + if (m_logger != null) + { + string args = "NOT IMPLEMENTED"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.SendKey(" + args + ")")); + } + return CommandStatus.NotSupported; + } + + /// + /// + /// + /// + public CommandStatus SendOpcInformation(bool doopc, object o) + { + if (m_logger != null) + { + string args = doopc.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.SendOpcInformation(" + args + ")")); + } + + ushort len = 0; + + if (o != null) + throw new Exception("SendOpcInformation() - non-null OPC information not supported"); + + using (Command cmd = new Command(ScsiCommandCode.SendOpcInformation, 10, len, Command.CmdDirection.Out, 60)) + { + if (doopc) + cmd.SetCDB8(1, 1); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus SetCdSpeed(RotationalControl ctrl, ushort read, ushort write) + { + if (m_logger != null) + { + string args = ctrl.ToString() + ", " + read.ToString() + ", " + write.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.SetCdSpeed(" + args + ")")); + } + using (Command cmd = new Command(ScsiCommandCode.SetCdSpeed, 12, 0, Command.CmdDirection.None, 2)) + { + cmd.SetCDB8(1, (byte)ctrl); + cmd.SetCDB16(2, read); + cmd.SetCDB16(4, write); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus SetReadAhead(uint trigger, uint lba) + { + if (m_logger != null) + { + string args = trigger.ToString() + ", " + lba.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.SetReadAhead(" + args + ")")); + } + using (Command cmd = new Command(ScsiCommandCode.SetReadAhead, 12, 0, Command.CmdDirection.None, 2)) + { + cmd.SetCDB32(2, trigger); + cmd.SetCDB32(6, lba); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus SetStreaming(SpeedDescriptor desc) + { + if (m_logger != null) + { + string args = "desc"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.SetStreaming(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.SetStreaming, 12, 28, Command.CmdDirection.Out, 2)) + { + cmd.SetCDB8(8, 0); // Performance Descriptor + cmd.SetCDB16(9, 28); // Length of the performance descriptor + + byte b = 0; + if (desc.Exact) + b |= 0x02; + int wrc = (int)(desc.WRC) ; + b |= (byte)(wrc << 3); + cmd.SetBuffer8(0, b); // Control info, byte 0 + + cmd.SetBuffer32(4, 0); // Start LBA + cmd.SetBuffer32(8, (uint)desc.EndLBA); // End LBA + cmd.SetBuffer32(12, (uint)desc.ReadSpeed); // Read size + cmd.SetBuffer32(16, 1000); // Read time + cmd.SetBuffer32(20, (uint)desc.WriteSpeed); // Write size + cmd.SetBuffer32(24, 1000); // Write time + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus SetStreaming(RotationalControl rot, int startlba, int endlba, int readsize, int readtime, int writesize, int writetime) + { + if (m_logger != null) + { + string args = "desc"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.SetStreaming(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.SetStreaming, 12, 28, Command.CmdDirection.Out, 60*5)) + { + cmd.SetCDB8(8, 0); // Performance Descriptor + cmd.SetCDB16(9, 28); // Length of the performance descriptor + + byte b = 0; + int wrc = (int)rot; + b |= (byte)(wrc << 3); + cmd.SetBuffer8(0, b); // Control info, byte 0 + + cmd.SetBuffer32(4, (uint)startlba); // Start LBA + cmd.SetBuffer32(8, (uint)endlba); // End LBA + cmd.SetBuffer32(12, (uint)readsize); // Read size + cmd.SetBuffer32(16, (uint)readtime); // Read time + cmd.SetBuffer32(20, (uint)writesize); // Write size + cmd.SetBuffer32(24, (uint)writetime); // Write time + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus StartStopUnit(bool immd, PowerControl pc, StartState state) + { + if (m_logger != null) + { + string args = immd.ToString() + ", " + pc.ToString() + ", " + state.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.StartStopUnit(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.StartStopUnit, 6, 0, Command.CmdDirection.None, 30)) + { + if (immd) + cmd.SetCDB8(1, 1) ; + + byte b = (byte)(((byte)pc << 4) | ((byte)state)) ; + cmd.SetCDB8(4, b) ; + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus StopPlayScan() + { + using (Command cmd = new Command(ScsiCommandCode.StopPlayScan, 10, 0, Command.CmdDirection.None, 30)) + { + if (m_logger != null) + { + string args = ""; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.StopPlayScan(" + args + ")")); + } + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus SynchronizeCache(bool immd) + { + if (m_logger != null) + { + string args = ""; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.SynchronizeCache(" + args + ")")); + } + using (Command cmd = new Command(ScsiCommandCode.SyncronizeCache, 10, 0, Command.CmdDirection.None, 30 * 60)) + { + if (immd) + cmd.SetCDB8(1, 2); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus TestUnitReady(out bool ready) + { + if (m_logger != null) + { + string args = "out ready"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 9, "Bwg.Scsi.Device.TestUnitReady(" + args + ")")); + } + + ready = true; + using (Command cmd = new Command(ScsiCommandCode.TestUnitReady, 6, 0, Command.CmdDirection.None, 60)) + { + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + { + ready = false ; + + if (st == CommandStatus.DeviceFailed && GetSenseKey() == SenseKeyType.NotReady) + st = CommandStatus.Success; + + return st; + } + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus Verify(int start, int size) + { + if (m_logger != null) + { + string args = start.ToString() + ", " + size.ToString(); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.Verify(" + args + ")")); + } + + using (Command cmd = new Command(ScsiCommandCode.Verify, 10, 2048, Command.CmdDirection.None, 60 * 60)) + { + cmd.SetCDB32(2, (uint)start); + cmd.SetCDB16(7, (ushort)size); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + + return CommandStatus.Success; + } + + + /// + /// This method requests that the SCSI device transfer data from the given buffer to the device + /// + /// If true, the data is forced from the media and cannot be read from the cache + /// If true, this is a streaming read + /// The starting logical address for the data + /// the size of the sector data in bytes + /// The length of the data to write in sectors + /// The data buffer to received the data + /// + public CommandStatus Write(bool force, bool streaming, int lba, int sector_size, int length, ref byte[] data) + { + if (m_logger != null) + { + string args = force.ToString() + ", " + streaming.ToString() + ", " + lba.ToString() + ", " + length.ToString() + ", buffer"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.Write(" + args + ")")); + } + + Debug.Assert(length == data.GetLength(0) * sector_size); + + fixed (byte *bptr = &data[0]) + { + IntPtr bufptr = new IntPtr(bptr); + if (streaming || length > 65535) + { + using (Command cmd = new Command(ScsiCommandCode.Write12, 12, bufptr, length * sector_size, Command.CmdDirection.Out, 5 * 60)) + { + if (force) + cmd.SetCDB8(1, 4); // Set the FUA bit + + cmd.SetCDB32(2, lba); + cmd.SetCDB32(6, length); + + if (streaming) + cmd.SetCDB8(10, 0x80); // Set the streaming bit + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + } + else + { + using (Command cmd = new Command(ScsiCommandCode.Write, 10, bufptr, length * sector_size, Command.CmdDirection.Out, 5 * 60)) + { + if (force) + cmd.SetCDB8(1, 4); // Set the FUA bit + + cmd.SetCDB32(2, lba); + cmd.SetCDB16(7, (ushort)length); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + return st; + } + } + } + + return CommandStatus.Success; + } + + /// + /// Write data to the device into a memory buffer + /// + /// If true, the data is forced from the media and cannot be read from the cache + /// If true, this is a streaming read + /// The starting logical address for the data + /// the size of a sector in bytes + /// The length of the data to write in sectors + /// The buffer to receive the data + /// + public CommandStatus Write(bool force, bool streaming, int lba, int sector_size, int length, IntPtr data) + { + if (m_logger != null) + { + string args = force.ToString() + ", " + streaming.ToString() + ", " + lba.ToString() + ", " + length.ToString() + ", IntPtr, "; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.Write(" + args + ")")); + } + + if (streaming || length > 65535) + { + using (Command cmd = new Command(ScsiCommandCode.Write12, 12, data, length * sector_size, Command.CmdDirection.Out, 5 * 60)) + { + if (force) + cmd.SetCDB8(1, 4); // Set the FUA bit + + cmd.SetCDB32(2, lba); + cmd.SetCDB32(6, length); + + if (streaming) + cmd.SetCDB8(10, 0x80); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + { + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 4, "Write failed at lba " + lba.ToString())); + return st; + } + } + } + else + { + using (Command cmd = new Command(ScsiCommandCode.Write, 10, data, length * sector_size, Command.CmdDirection.Out, 5 * 60)) + { + if (force) + cmd.SetCDB8(1, 4); // Set the FUA bit + + cmd.SetCDB32(2, lba); + cmd.SetCDB16(7, (ushort)length); + + CommandStatus st = SendCommand(cmd); + if (st != CommandStatus.Success) + { + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 4, "Write failed at lba " + lba.ToString())); + return st; + } + } + } + + return CommandStatus.Success; + } + + /// + /// + /// + /// + public CommandStatus WriteVerify() + { + if (m_logger != null) + { + string args = "NOT IMPLEMENTED"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.WriteVerify(" + args + ")")); + } + return CommandStatus.NotSupported; + } + + /// + /// + /// + /// + public CommandStatus WriteBuffer() + { + if (m_logger != null) + { + string args = "NOT IMPLEMENTED"; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 8, "Bwg.Scsi.Device.WriteBuffer(" + args + ")")); + } + return CommandStatus.NotSupported; + } + + #endregion + } +} diff --git a/Bwg.Scsi/DeviceInfo.cs b/Bwg.Scsi/DeviceInfo.cs new file mode 100644 index 0000000..3296b4f --- /dev/null +++ b/Bwg.Scsi/DeviceInfo.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Scsi +{ + /// + /// This class contains information about a burner device that is useful to have + /// even when the device it not open + /// + public class DeviceInfo + { + #region private member variables + /// + /// The device name for the device + /// + private string m_device_name; + + /// + /// The device latter for the device + /// + private string m_device_letter; + + /// + /// The result of an inquiry + /// + private InquiryResult m_inqury_result; + + #endregion + + #region static private member variables + /// + /// The list of devices found and created. For each device only a single DeviceInfo object + /// will ever be created and this list holds the objects created to date + /// + private static IList m_devices = new List(); + #endregion + + #region constructor + /// + /// Create a new device info object given the device name and drive letter for the device + /// + /// the device name + /// the drive letter + private DeviceInfo(string name, string letter) + { + m_device_name = name; + m_device_letter = letter; + } + #endregion + + #region public properties + /// + /// This property returns the drive letter associated with the device + /// + public string DeviceLetter + { + get { return m_device_letter; } + } + + /// + /// This property returns the NT device name associated with the device + /// + public string DeviceName + { + get { return m_device_name; } + } + + /// + /// This property returns a short description of the drive including drive letter + /// and NT device name. + /// + public string ShortDesc + { + get { return m_device_letter + " ( " + m_device_name + " )"; } + } + + /// + /// This property return a long description of the drive including the drive letter, + /// NT device name, vendor ID and product ID. + /// + public string LongDesc + { + get + { + string result = m_device_letter + " "; + // result += " ( " + m_device_name + " ) "; + result += "[" + m_inqury_result.VendorIdentification + " " + + m_inqury_result.ProductIdentification + " " + + m_inqury_result.FirmwareVersion + "]"; + return result; + } + } + + /// + /// This property returns the inquiry data from the inquiry request + /// + public InquiryResult InquiryData + { + get { return m_inqury_result; } + } + #endregion + + #region public methods + + /// + /// This method returns a string representation of this object + /// + /// string representatation of the object + public override string ToString() + { + return m_device_letter + " (" + m_inqury_result.VendorIdentification.Trim() + " " + m_inqury_result.ProductIdentification.Trim() + ")"; + } + + /// + /// Extract the information we want to keep around after we close the device. + /// + /// the open device we are going to query + /// true if we got the info, false otherwise + public bool ExtractInfo(Device dev) + { + if (dev.Inquiry(out m_inqury_result) != Device.CommandStatus.Success) + return false; + + if (!m_inqury_result.Valid) + return false; + + if (m_inqury_result.PeripheralQualifier != 0) + return false; + + if (m_inqury_result.PeripheralDeviceType != Device.MMCDeviceType) + return false; + + return true; + } + #endregion + + #region public static methods + /// + /// This method returns or creates a unique device info structure based on the name and the + /// drive letter for a given drive. + /// + /// the NT name of the drive + /// the drive letter for the drive + /// the single DeviceInfo object that represents this drive + public static DeviceInfo CreateDevice(string name, string letter) + { + foreach (DeviceInfo info in m_devices) + { + if (info.DeviceName == name && info.DeviceLetter == letter) + return info; + } + + DeviceInfo newdev = new DeviceInfo(name, letter); + m_devices.Add(newdev); + return newdev; + } + #endregion + } +} diff --git a/Bwg.Scsi/DeviceManager.cs b/Bwg.Scsi/DeviceManager.cs new file mode 100644 index 0000000..46dc819 --- /dev/null +++ b/Bwg.Scsi/DeviceManager.cs @@ -0,0 +1,228 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices ; +using System.IO; +using Bwg.Logging; + +namespace Bwg.Scsi +{ + /// + /// This class manages the burner devices that are in the system. + /// + public unsafe class DeviceManager + { + #region external functions + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern uint QueryDosDevice(string name, IntPtr buffer, uint size); + #endregion + + #region private member variables + + /// + /// The logger for logging messages + /// + private Logger m_logger; + + /// + /// This list contains the device names found in the system + /// + private IList m_devices_found; + #endregion + + #region constructor + /// + /// This constructor creates a new device manager object. + /// + /// the logger to use for logging messages + public DeviceManager(Logger l) + { + m_logger = l; + m_devices_found = new List(); + } + #endregion + + #region public variables + /// + /// This event is fired just before a rescan of the devices in the system. + /// + public EventHandler BeforeScan; + + /// + /// This event is triggered just after a rescan of devices in the system. + /// + public EventHandler AfterScan; + #endregion + + #region public properties + /// + /// This property returns the list of devices found as a result of the most recent + /// scan. + /// + public IList DevicesFound + { + get { return m_devices_found; } + } + + /// + /// This property returns the logger assocaited with the device manager + /// + public Logger MyLogger + { + get { return m_logger; } + } + #endregion + + #region public methods + /// + /// This method scans the current machine for all CDROM devices found in the system. + /// + public void ScanForDevices() + { + if (BeforeScan != null) + { + DeviceManagerRescanArgs args = new DeviceManagerRescanArgs(); + BeforeScan(this, args); + } + + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 0, "Scanning for devices ... ")) ; + + OperatingSystem os = Environment.OSVersion; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Info, 0, "Operating System: " + os.ToString())); + m_logger.LogMessage(new UserMessage(UserMessage.Category.Info, 0, "Platform: " + os.Platform.ToString())); + + int ossize = 32 ; + if (IntPtr.Size == 8) + ossize = 64 ; + + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 0, "OS Word Size: " + ossize.ToString())) ; + + m_devices_found.Clear(); + for (int i = 0; i < 100; i++) + { + uint dlev = (uint)((i > 5) ? 9 : 8); + string name = "\\\\.\\CDROM" + i.ToString(); + + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, dlev, "Checking device " + name + " ... ")); + + Device dev = new Device(m_logger) ; + if (!dev.Open(name)) + { + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, dlev, " ... device open failed")); + continue ; + } + + string letter = GetLetterFromDeviceName(name); + + DeviceInfo info = DeviceInfo.CreateDevice(name, letter); + if (!info.ExtractInfo(dev)) + { + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, dlev, " ... cannot extract inquiry information from the drive")); + + string str = "The drive '" + letter + "' (" + name + ") is a CD/DVD driver, but is not a valid MMC device."; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Error, 0, str)); + str = "This drive is not supported by BwgBurn and is probably an older device."; + m_logger.LogMessage(new UserMessage(UserMessage.Category.Error, 0, str)); + continue; + } + + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, dlev, " ... device added to device list")); + m_devices_found.Add(info) ; + dev.Close() ; + } + + if (AfterScan != null) + { + DeviceManagerRescanArgs args = new DeviceManagerRescanArgs(); + AfterScan(this, args); + } + + string devlist = string.Empty; + foreach (DeviceInfo info in m_devices_found) + { + if (devlist.Length > 0) + devlist += ", "; + devlist += info.ShortDesc; + } + m_logger.LogMessage(new UserMessage(UserMessage.Category.Debug, 0, "Found devices ... " + devlist)); + + foreach (DeviceInfo info in m_devices_found) + { + UserMessage m = new UserMessage(UserMessage.Category.Debug, 6, "Found Device: " + info.DeviceLetter) ; + m_logger.LogMessage(m); + + m = new UserMessage(UserMessage.Category.Debug, 6, " NT Name = " + info.DeviceName); + m_logger.LogMessage(m); + + m = new UserMessage(UserMessage.Category.Debug, 6, " Vendor = " + info.InquiryData.VendorIdentification.Trim()); + m_logger.LogMessage(m); + + m = new UserMessage(UserMessage.Category.Debug, 6, " Product = " + info.InquiryData.ProductIdentification.Trim()); + m_logger.LogMessage(m); + + m = new UserMessage(UserMessage.Category.Debug, 6, " Revision = " + info.InquiryData.ProductRevision.Trim()); + m_logger.LogMessage(m); + } + } + #endregion + + #region private methods + + private static string GetNtDeviceNameForDrive(string name) + { + string result; + const int buflen = 512; + uint ret; + byte[] buffer = new byte[buflen]; + + if (name.EndsWith("\\")) + name = name.Substring(0, name.Length - 1); + + IntPtr ptr = Marshal.AllocHGlobal(buflen); + { + ret = QueryDosDevice(name, ptr, buflen / 2); + Marshal.Copy(ptr, buffer, 0, buflen); + + int totallen = buflen; + for (int i = 0; i < buflen - 2; i++) + { + if (buffer[i] == 0 && buffer[i + 1] == 0) + { + totallen = i + 1; + break; + } + } + result = Encoding.Unicode.GetString(buffer, 0, totallen); + } + + return result; + } + + /// + /// Return the drive letter for a drive given its NT name + /// + /// the NT name + /// the drive letter + public static string GetLetterFromDeviceName(string devname) + { + if (devname.StartsWith("\\\\.\\")) + devname = devname.Substring(4); + + foreach (DriveInfo info in DriveInfo.GetDrives()) + { + if (info.DriveType == DriveType.CDRom) + { + string ntname = GetNtDeviceNameForDrive(info.Name); + if (ntname.StartsWith("\\Device\\")) + ntname = ntname.Substring(8); + + if (ntname.ToLower() == devname.ToLower()) + return info.Name; + } + } + + return string.Empty; + } + #endregion + } +} diff --git a/Bwg.Scsi/DeviceManagerRescanArgs.cs b/Bwg.Scsi/DeviceManagerRescanArgs.cs new file mode 100644 index 0000000..6440ed2 --- /dev/null +++ b/Bwg.Scsi/DeviceManagerRescanArgs.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Scsi +{ + /// + /// This class represents the arguments to the scan event in the + /// device manager. + /// + public class DeviceManagerRescanArgs : EventArgs + { + } +} diff --git a/Bwg.Scsi/DiskInformation.cs b/Bwg.Scsi/DiskInformation.cs new file mode 100644 index 0000000..95ce5bc --- /dev/null +++ b/Bwg.Scsi/DiskInformation.cs @@ -0,0 +1,409 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for DotNet +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; + +namespace Bwg.Scsi +{ + /// + /// This class contains the results of the ReadDiskInformation SCSI command + /// + public class DiskInformation : Result + { + #region Public types + /// + /// Type defining the state of the last session on the disk + /// + public enum SessionStateType + { + /// + /// The session is empty + /// + EmptySession = 0, + + /// + /// The session is incomplete + /// + IncompleteSession = 1, + + /// + /// The session is damaged + /// + DamagedSession = 2, + + /// + /// The session is complete + /// + CompleteSession = 3 + } ; + + /// + /// Type defining the status of the disk + /// + public enum DiskStatusType + { + /// + /// The disk is empty + /// + EmptyDisk = 0, + + /// + /// The disk is incomplete + /// + IncompleteDisk = 1, + + /// + /// The disk is finalized + /// + FinalizedDisk = 2, + + /// + /// The disk is in an other state which is dependent on the media type + /// + RandomlyRecordable = 3 + } ; + + /// + /// The state of a background format + /// + public enum BackgroundFormatStatusType + { + /// + /// No background format was started, or it does not apply + /// + NotStartedOrDoesNotApply = 0, + + /// + /// A background format was started, did not complete, and is not running + /// + StartedButNotRunning = 1, + + /// + /// A background format is in progress and is not complete + /// + InProgress = 2, + + /// + /// A background format was started and is complete + /// + Complete = 3 + } ; + #endregion + + #region Public Data Members + + private readonly bool m_erasable; + + /// + /// If true, the disk is erasable. If false the disk cannot be erased + /// + public bool Erasable + { + get { return m_erasable; } + } + + private readonly SessionStateType m_session_state; + + /// + /// The state of the last session on the disk + /// + public SessionStateType SessionState + { + get { return m_session_state; } + } + + private readonly DiskStatusType m_disk_status; + + + /// + /// The status of the disk + /// + public DiskStatusType DiskStatus + { + get { return m_disk_status; } + } + + + private readonly byte m_first_track; + + /// + /// Number of the first track on the disk + /// + public byte FirstTrack + { + get { return m_first_track; } + } + + + private readonly ushort m_session_count; + + /// + /// Number of sessions on the disk + /// + public ushort SessionCount + { + get { return m_session_count; } + } + + + private readonly ushort m_first_track_in_last_session; + + /// + /// The first track number in the last session + /// + public ushort FirstTrackInLastSession + { + get { return m_first_track_in_last_session; } + } + + + private readonly ushort m_last_track_in_last_session; + + /// + /// The last track number in the last session + /// + public ushort LastTrackInLastSession + { + get { return m_last_track_in_last_session; } + } + + + private readonly bool m_disk_id_valid; + + /// + /// If true, the disk ID was valid + /// + public bool DiskIdValid + { + get { return m_disk_id_valid; } + } + + + private readonly uint m_disk_identification; + + /// + /// The four byte disk id + /// + public uint DiskIdentification + { + get { return m_disk_identification; } + } + + + private readonly bool m_disk_bar_code_valid; + + /// + /// If true, the disk bar code was valid + /// + public bool DiskBarCodeValid + { + get { return m_disk_bar_code_valid; } + } + + + private readonly byte[] m_disk_bar_code; + + /// + /// The eight byte disk bar code + /// + public byte[] DiskBarCode + { + get { return m_disk_bar_code; } + } + + + private readonly bool m_unrestricted_disk_use; + + /// + /// If true, this disk is unrestriced it its use + /// + public bool UnrestrictedDiskUse + { + get { return m_unrestricted_disk_use; } + } + + + private readonly bool m_disk_application_code_valid; + + /// + /// If true, the disk application code is valid + /// + public bool DiskApplicationCodeValid + { + get { return m_disk_application_code_valid; } + } + + + private readonly byte m_disk_application_code; + + /// + /// The disk application code + /// + public byte DiskApplicationCode + { + get { return m_disk_application_code; } + } + + + private readonly bool m_dirty_bit; + + /// + /// The Dbit (dirty bit) for MRW media + /// + public bool DirtyBit + { + get { return m_dirty_bit; } + } + + + private readonly BackgroundFormatStatusType m_background_format_status; + + /// + /// The status of a background format + /// + public BackgroundFormatStatusType BackgroundFormatStatus + { + get { return m_background_format_status; } + } + + + private readonly MinuteSecondFrame m_last_session_lead_in_start_address; + + /// + /// The last session leadin start address, media dependent, see SCSI-3 MMC spec + /// + public MinuteSecondFrame LastSessionLeadInStartAddress + { + get { return m_last_session_lead_in_start_address; } + } + + + private readonly MinuteSecondFrame m_last_possible_leadout_start_address; + + /// + /// The last possible leadout start address, media dependent, see SCSI-3 MMC spec + /// + public MinuteSecondFrame LastPossibleLeadoutStartAddress + { + get { return m_last_possible_leadout_start_address; } + } + + + /// + /// The OPC table + /// + public IList OpcTable; + + #endregion + + #region constructor + /// + /// The constructor for the DiskInformation class. It parses the information from the + /// memory buffer that contains the raw SCSI result. + /// + /// The buffer containing the raw SCSI result + /// The size of the raw SCSI result buffer + public DiskInformation(IntPtr buffer, int size) : base(buffer, size) + { + ushort len = Get16(0); + byte b = Get8(2); + + if ((b & 0x10) != 0) + m_erasable = true; + else + m_erasable = false; + + m_session_state = (SessionStateType)((b >> 2) & 0x03); + m_disk_status = (DiskStatusType)(b & 0x03); + + m_first_track = Get8(3); + m_session_count = (ushort)(Get8(4) | (Get8(9) << 8)); + m_first_track_in_last_session = (ushort)(Get8(5) | (Get8(10) << 8)); + m_last_track_in_last_session = (ushort)(Get8(6) | (Get8(11) << 8)); + + b = Get8(7); + if ((b & 0x80) != 0) + { + m_disk_id_valid = true; + m_disk_identification = Get32(12); + } + else + m_disk_id_valid = false; + + if ((b & 0x40) != 0) + { + m_disk_bar_code_valid = true; + m_disk_bar_code = new byte[8]; + for (int i = 24; i <= 31; i++) + m_disk_bar_code[i - 24] = Get8(i); + } + else + { + m_disk_bar_code_valid = false; + m_disk_bar_code = null; + } + + if ((b & 0x20) != 0) + m_unrestricted_disk_use = true; + else + m_unrestricted_disk_use = false; + + if ((b & 0x10) != 0) + { + m_disk_application_code_valid = true; + m_disk_application_code = Get8(32); + } + else + m_disk_application_code_valid = false; + + if ((b & 0x04) != 0) + m_dirty_bit = true; + else + m_dirty_bit = false; + + m_background_format_status = (BackgroundFormatStatusType)(b & 0x03); + + m_last_session_lead_in_start_address = new MinuteSecondFrame(Get8(16) * 60 + Get8(17), Get8(18), Get8(19)); + m_last_possible_leadout_start_address = new MinuteSecondFrame(Get8(20) * 60 + Get8(21), Get8(22), Get8(23)); + + OpcTable = new List(); + + if (BufferSize > 33) + { + byte cnt = Get8(33); + int offset = 34; + + for(byte i = 0 ; i < cnt && offset < BufferSize ; i++) + { + OpcTableEntry entry = new OpcTableEntry(Buffer, BufferSize, offset); + OpcTable.Add(entry); + offset += 8; + } + } + } + #endregion + } +} diff --git a/Bwg.Scsi/EventStatusNotification.cs b/Bwg.Scsi/EventStatusNotification.cs new file mode 100644 index 0000000..17b7f5c --- /dev/null +++ b/Bwg.Scsi/EventStatusNotification.cs @@ -0,0 +1,171 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for DotNet +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; + +namespace Bwg.Scsi +{ + /// + /// This class contains information about event status communicated from the device + /// via the GetEventStatusNotification SCSI command. + /// + public class EventStatusNotification : Result + { + #region public types + + /// + /// This field indicates the type of event + /// + public enum EventType + { + /// + /// No event was returned + /// + NoEvent = 0x00, + + /// + /// There was an operation change + /// + OperationalChange = 0x01, + + /// + /// There was a change in power management status + /// + PowerManagement = 0x02, + + /// + /// There was an external request to the device + /// + ExternalRequest = 0x03, + + /// + /// The status of the media has changed + /// + Media = 0x04, + + /// + /// There was a change in the multiple initiator device + /// + MultipleInitiator = 0x05, + + /// + /// There was a change in the busy state of the device + /// + DeviceBusy = 0x06, + + /// + /// This value is reserved and should not be returned + /// + Reserved = 0x07, + } ; + #endregion + + #region public member variables + + /// + /// This property returns true if an event is available + /// + public readonly bool EventAvailable; + + /// + /// This property is a byte that contains a mask of the supported event classes + /// + public readonly byte SupportedEventClasses; + + /// + /// This property contains the notification class of the specific event that has + /// occurred. + /// + public readonly EventType NotificationClass; + + /// + /// This array contains the specific data returned describing the event. The format of this + /// data is described in the SCSI-3 MMC specification. + /// + public readonly byte [] EventData; + #endregion + + #region constructor + /// + /// This is the contructor for this class which contains information about any events that + /// have occurred in the SCSI device. + /// + /// Pointer to a memory area containing the reply from the SCSI device + /// The size of the memory area containing the reply from the SCSI device + public EventStatusNotification(IntPtr buffer, int size) : base(buffer, size) + { + SupportedEventClasses = Marshal.ReadByte(buffer, 3); + + byte b = Marshal.ReadByte(buffer, 2); + if ((b & 0x80) != 0) + { + EventAvailable = false; + } + else + { + ushort len = Get16(0); + EventAvailable = true; + NotificationClass = (EventType)(b & 0x03); + EventData = new byte[len]; + for (int i = 0; i < len; i++) + EventData[i] = Marshal.ReadByte(buffer, 4 + i); + } + } + #endregion + + #region public properties + /// + /// This property returns true if Operational Events are supported on this device + /// + public bool OperationalChangeEventsSupported { get { return (SupportedEventClasses & 0x02) != 0; } } + + /// + /// This property returns true if Power Management Events are supported on this device + /// + public bool PowerManagementEventsSupported { get { return (SupportedEventClasses & 0x04) != 0; } } + + /// + /// This property returns true if External Request Events are supported on this device + /// + public bool ExternalRequestEventsSupported { get { return (SupportedEventClasses & 0x08) != 0; } } + + /// + /// This property returns true if Media Events are supported on this device + /// + public bool MediaEventsSupported { get { return (SupportedEventClasses & 0x10) != 0; } } + + /// + /// This property returns true if Multiple Initiator Events are supported on this device + /// + public bool MultiInitiatorEventsSupported { get { return (SupportedEventClasses & 0x20) != 0; } } + + /// + /// This property returns true if Device Busy Events are supported on this device + /// + public bool DeviceBusyEventsSupported { get { return (SupportedEventClasses & 0x40) != 0; } } + #endregion + } +} diff --git a/Bwg.Scsi/Feature.cs b/Bwg.Scsi/Feature.cs new file mode 100644 index 0000000..99b7e46 --- /dev/null +++ b/Bwg.Scsi/Feature.cs @@ -0,0 +1,438 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Scsi +{ + /// + /// This class describes a single SCSI feature. The data associated with the feature is + /// stored as a series of bytes that must be interpreted by the calling code. + /// + public class Feature : Result + { + #region Feature Numbers + /// + /// This enumerated type indicates the type of the feature. + /// + public enum FeatureType : ushort + { + /// + /// The ProfileList feature (see the SCSI-3 MMC specification) + /// + ProfileList = 0x0000, + + /// + /// The Core feature (see the SCSI-3 MMC specification) + /// + Core = 0x0001, + + /// + /// The Morphing feature (see the SCSI-3 MMC specification) + /// + Morphing = 0x0002, + + /// + /// The RemovableMedia feature (see the SCSI-3 MMC specification) + /// + RemovableMedia = 0x0003, + + /// + /// The WriteProtect feature (see the SCSI-3 MMC specification) + /// + WriteProtect = 0x0004, + + /// + /// The RandomReadable feature (see the SCSI-3 MMC specification) + /// + RandomReadable = 0x0010, + + /// + /// The MultiRead feature (see the SCSI-3 MMC specification) + /// + MultiRead = 0x001d, + + /// + /// The CDRead feature (see the SCSI-3 MMC specification) + /// + CDRead = 0x001e, + + /// + /// The DVDRead feature (see the SCSI-3 MMC specification) + /// + DVDRead = 0x001f, + + /// + /// The RandomWritable feature (see the SCSI-3 MMC specification) + /// + RandomWritable = 0x0020, + + /// + /// The IncrementalStreamWritable feature (see the SCSI-3 MMC specification) + /// + IncrementalStreamWritable = 0x0021, + + /// + /// The SectorErasable feature (see the SCSI-3 MMC specification) + /// + SectorErasable = 0x0022, + + /// + /// The Formattable feature (see the SCSI-3 MMC specification) + /// + Formattable = 0x0023, + + /// + /// The HardwareDefectManagement feature (see the SCSI-3 MMC specification) + /// + HardwareDefectManagement = 0x0024, + + /// + /// The WriteOnce feature (see the SCSI-3 MMC specification) + /// + WriteOnce = 0x0025, + + /// + /// The RestrictedOverwrite feature (see the SCSI-3 MMC specification) + /// + RestrictedOverwrite = 0x0026, + + /// + /// The CDRWCavWrite feature (see the SCSI-3 MMC specification) + /// + CDRWCavWrite = 0x0027, + + /// + /// The MRW feature (see the SCSI-3 MMC specification) + /// + MRW = 0x0028, + + /// + /// The EnhancedDefectReporting feature (see the SCSI-3 MMC specification) + /// + EnhancedDefectReporting = 0x0029, + + /// + /// The DVDPlusRW feature (see the SCSI-3 MMC specification) + /// + DVDPlusRW = 0x002a, + + /// + /// The DVDPlusR feature (see the SCSI-3 MMC specification) + /// + DVDPlusR = 0x002b, + + /// + /// The RigidRestrictedOverwrite feature (see the SCSI-3 MMC specification) + /// + RigidRestrictedOverwrite = 0x002c, + + /// + /// The CDTrackAtOnce feature (see the SCSI-3 MMC specification) + /// + CDTrackAtOnce = 0x002d, + + /// + /// The CDSessionAtOnce feature (see the SCSI-3 MMC specification) + /// + CDSessionAtOnce = 0x002e, + + /// + /// The DVDMinusR_DVDMinusRW feature (see the SCSI-3 MMC specification) + /// + DVDMinusR_DVDMinusRW = 0x002f, + + /// + /// The DDCDRead feature (see the SCSI-3 MMC specification) + /// + DDCDRead = 0x0030, + + /// + /// The DDCDMinusR feature (see the SCSI-3 MMC specification) + /// + DDCDMinusR = 0x0031, + + /// + /// The DDCDMinusRW feature (see the SCSI-3 MMC specification) + /// + DDCDMinusRW = 0x0032, + + /// + /// The layer jump recording feature (see the SCSI MMC-5 specification) + /// + LayerJumpRecording = 0x0033, + + /// + /// The CDRWMediaWriteSupport feature (see the SCSI-3 MMC specification) + /// + CDRWMediaWriteSupport = 0x0037, + + /// + /// + /// + BD_RPseudoOverWrite = 0x0038, + + /// + /// The dual layer feature for DVD+RW + /// + DvdPlusRWDualLayer = 0x003a, + + /// + /// The dual layer featuer for DVD+R + /// + DvdPlusRDualLayer = 0x003b, + + /// + /// + /// + BDRead = 0x0040, + + /// + /// + /// + BDWrite = 0x0041, + + /// + /// + /// + TSR = 0x0042, + + /// + /// + /// + HdDvdRead = 0x0050, + + /// + /// + /// + HdDvdWrite = 0x0051, + + /// + /// + /// + OldHybridDisk = 0x0052, + + /// + /// + /// + HybridDisk = 0x0080, + + /// + /// The PowerMangaementFeature feature (see the SCSI-3 MMC specification) + /// + PowerMangaementFeature = 0x0100, + + /// + /// The S_M_A_R_T feature (see the SCSI-3 MMC specification) + /// + S_M_A_R_T = 0x0101, + + /// + /// The EmbeddedChanger feature (see the SCSI-3 MMC specification) + /// + EmbeddedChanger = 0x0102, + + /// + /// The CDAudioExternalPlay feature (see the SCSI-3 MMC specification) + /// + CDAudioExternalPlay = 0x0103, + + /// + /// The MicrocodeUpgrade feature (see the SCSI-3 MMC specification) + /// + MicrocodeUpgrade = 0x0104, + + /// + /// The Timeout feature (see the SCSI-3 MMC specification) + /// + Timeout = 0x0105, + + /// + /// The DVDCSS feature (see the SCSI-3 MMC specification) + /// + DVDCSS = 0x0106, + + /// + /// The RealTimeStreaming feature (see the SCSI-3 MMC specification) + /// + RealTimeStreaming = 0x0107, + + /// + /// The LogicalUnitSerialNumber feature (see the SCSI-3 MMC specification) + /// + LogicalUnitSerialNumber = 0x0108, + + /// + /// The MediaSerialNumber feature (see the SCSI-3 MMC specification) + /// + MediaSerialNumber = 0x0109, + + /// + /// The DiskControlBlock feature (see the SCSI-3 MMC specification) + /// + DiskControlBlock = 0x010A, + + /// + /// The DVD_CPRM feature (see the SCSI-3 MMC specification) + /// + DVD_CPRM = 0x010B, + + /// + /// The FirmwareInformation feature (see the SCSI-3 MMC specification) + /// + FirmwareInformation = 0x010C, + + /// + /// + /// + AACS = 0x010d, + + /// + /// + /// + VCPS = 0x0110 + } ; + #endregion + + #region private variables + + /// + /// The feature code for this feature + /// + private FeatureType m_code; + + /// + /// The verion for this feature + /// + private byte m_version; + + /// + /// If true, this feature is persistent (see SCSI MMC spec for more details) + /// + private bool m_persistent; + + /// + /// If true, this feature is current (see SCSI MMC spec for more details) + /// + private bool m_current; + + /// + /// The raw data assocaited with the feature. This data may be invalid if the + /// feature is invalid. + /// + private byte[] m_data; + + #endregion + + #region constructor + /// + /// This is a constructor for the SCSI feature. It takes data from a raw buffer and + /// creates the SCSI featuer object + /// + /// Pointer to the start of the raw reply buffer + /// Size of the raw reply buffer + /// The offset to start parsing a SCSI feature from the reply buffer + public Feature(IntPtr buffer, int size, ref int offset) : base(buffer, size) + { + m_code = (FeatureType)Get16(offset); + offset += 2; + + byte b = Get8(offset); + offset++; + + if ((b & 0x01) != 0) + m_current = true; + else + m_current = false; + + if ((b & 0x02) != 0) + m_persistent = true; + else + m_persistent = false; + + m_version = (byte)((b >> 2) & 0x0f); + + byte len = Get8(offset); + offset++; + + m_data = new byte[len]; + for(int i = 0 ; i < len && offset < BufferSize ; i++) + { + m_data[i] = Get8(offset); + offset++; + } + } + #endregion + + #region public methods + /// + /// Return a string representing a human readable form of this object. + /// + /// string representing this object + public override string ToString() + { + return "Feature(type=" + m_code.ToString() + ")"; + } + #endregion + + #region public propreties + /// + /// Return the code associated with this feature + /// + public FeatureType Code { get { return m_code; } } + + /// + /// Return the version of the current feature + /// + public byte Version { get { return m_version; } } + + /// + /// Return the presistence state of the feature. If true, this feature is persistant + /// + public bool Persistent { get { return m_persistent; } } + + /// + /// Return the current flag associated with the feature. If true, this feature is currently active. + /// + public bool Current { get { return m_current; } } + + /// + /// This item contains the data from the feature + /// + public byte[] Data { get { return m_data; } } + + /// + /// This property returns the name of the feature. + /// + public string Name + { + get + { + return m_code.ToString(); + } + } + #endregion + } +} diff --git a/Bwg.Scsi/FeatureList.cs b/Bwg.Scsi/FeatureList.cs new file mode 100644 index 0000000..6e22463 --- /dev/null +++ b/Bwg.Scsi/FeatureList.cs @@ -0,0 +1,64 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Scsi +{ + /// + /// This class represents a list of features returned from the SCSI device. + /// + public class FeatureList : Result + { + /// + /// The profile associated with this feature list. + /// + public readonly ushort Profile; + + /// + /// The list of features returned from the device + /// + public readonly IList Features; + + /// + /// The constructure for the ScsiFeatureList object + /// + /// The pointer to the memory area containing the SCSI reply + /// The size of the SCSI reply + public FeatureList(IntPtr buffer, int size) : base(buffer, size) + { + Profile = Get16(6); + Features = new List() ; + + uint len = Get32(0); + int offset = 8; + while (offset < len + 8 && offset < size) + { + Feature f = new Feature(buffer, size, ref offset); + Features.Add(f); + } + } + } +} diff --git a/Bwg.Scsi/FileReader.cs b/Bwg.Scsi/FileReader.cs new file mode 100644 index 0000000..9f5594c --- /dev/null +++ b/Bwg.Scsi/FileReader.cs @@ -0,0 +1,142 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; +//using Bwg.Utils; + +namespace Bwg.Scsi +{ + /// + /// This class reads data from a file and sends this data to the buffered + /// write stream. + /// + public unsafe class FileReader + { + #region private member variables + /// + /// The OS file for reading data from a file into a memory buffer (IntPtr) + /// + private OsFileReader m_reader; + + /// + /// The buffer pool to receive the data + /// + private WriteBufferPool m_buffer_stream; + #endregion + + + /// + /// Create a new file reader that sends the data read to a buffer stream + /// object + /// + /// the buffer to receive the data + public FileReader(WriteBufferPool buffer) + { + m_buffer_stream = buffer; + m_reader = new OsFileReader(); + } + + /// + /// Open a file to read data from + /// + /// the name of the file + /// true if the file is opened, false otherwise + public bool Open(string filename) + { + return m_reader.Open(filename); + } + + /// + /// This method closes an open file. + /// + /// true if the file is closed sucessfully, false otherwise + public bool Close() + { + return m_reader.Close(); + } + + /// + /// This function reads data from the file and sends it to the + /// buffer stream to be sent to the burner. This function does not + /// return until all of the data from the file is read. + /// + /// + public bool ReadData(int sector_size, long lba) + { + bool shortbuf = false; + int NumberRead ; + int num_sectors ; + bool first = true; + + // This is the number of whole sectors that will fit into the buffer. + num_sectors = m_buffer_stream.PageSize / sector_size; + + while (true) + { + WriteBuffer buf ; + + buf = m_buffer_stream.GetFreeBuffer(); + if (!m_reader.ReadData(buf.BufferPtr, num_sectors * sector_size, out NumberRead)) + return false ; + + // If the read succeeded, but read zero bytes, we have reached the + // end of the file. + if (NumberRead == 0) + return true; + + if (shortbuf) + throw new Exception("read two short packets"); + + // Mark the buffer with the amount of data that has + // been read from the file + int sectors = NumberRead / sector_size ; + if ((NumberRead % sector_size) != 0) + { + shortbuf = true; + sectors++; + } + + if (first) + { + buf.SourceString = "First block from FileReader"; + buf.LogicalBlockAddress = lba; + first = false; + } + else + { + buf.SourceString = "Not first block from FileReader"; + buf.LogicalBlockAddress = long.MaxValue; + } + + buf.SectorSize = sector_size; + buf.DataSize = sectors; + + // Send the buffer to the device + m_buffer_stream.SendBufferToDevice(buf); + } + } + } +} diff --git a/Bwg.Scsi/FormatParameterList.cs b/Bwg.Scsi/FormatParameterList.cs new file mode 100644 index 0000000..3d32385 --- /dev/null +++ b/Bwg.Scsi/FormatParameterList.cs @@ -0,0 +1,343 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; + +namespace Bwg.Scsi +{ + /// + /// This class represents the list of format parameters that are presented to + /// the device when performing a FormatUnit() operation. + /// + public class FormatParameterList + { + /// + /// See SCSI MMC-4 specification + /// + public enum UnitFormatType : byte + { + /// + /// See SCSI MMC-4 specification + /// + FullFormat = 0, + + /// + /// See SCSI MMC-4 specification + /// + SpareAreaExpansion = 1, + + /// + /// See SCSI MMC-4 specification + /// + ZoneReformat = 4, + + /// + /// See SCSI MMC-4 specification + /// + ZoneFormat = 5, + + /// + /// See SCSI MMC-4 specification + /// + CD_DVD_RW_FullFormat = 0x10, + + /// + /// See SCSI MMC-4 specification + /// + CD_DVD_RW_GrowSession = 0x11, + + /// + /// See SCSI MMC-4 specification + /// + CD_DVD_RW_AddSession = 0x12, + + /// + /// See SCSI MMC-4 specification + /// + DVD_RW_QuickGrowLastSession = 0x13, + + /// + /// See SCSI MMC-4 specification + /// + DVD_RW_QuickAddSession = 0x14, + + /// + /// See SCSI MMC-4 specification + /// + DVD_RW_QuickFormat = 0x15, + + /// + /// See SCSI MMC-4 specification + /// + MRW_FullFormat = 0x24, // CD-RW NumberOfBlocks = 0xffffffff + // DVD+RW NumberOfBlocks = 0xffffffff + + /// + /// See SCSI MMC-4 specification + /// + DVD_Plus_RW_BasicFormat = 0x25, // NumberOfBlock = 0xffffffff + + /// + /// Basic format for DVD+RW (either SL or DL), see SCSI MMC-6 + /// + DVD_Plus_RW_BasicFormat_DL_SL = 0x26, + + /// + /// Format with spare areas for BluRay-RE + /// + BDRE_Format_With_Spare_Areas = 0x30, + + /// + /// Format without spare areas for BlueRay-RE + /// + BDRE_Format_Without_Spare_Areas = 0x31, + + /// + /// Format a BD-R disk with spare area + /// + BDR_Format_With_Spare_Areas = 0x32 + + } ; + + private byte m_flags; + + /// + /// The initializaion pattern for this format operation (not yet supported) + /// + public InitializationPattern IP; + + /// + /// The format type for this format operation + /// + public UnitFormatType FormatType; + + /// + /// The subtype for the format + /// + public byte FormatSubType; + + /// + /// The number of blocks for the format operation + /// + public uint NumberOfBlocks; + + /// + /// The format type dependent parameter + /// + public uint Parameter; + + /// + /// Constructor for the parameter list object + /// + public FormatParameterList() + { + m_flags = 0; + IP = null; + } + + /// + /// The size of the format parameters list in bytes, as it will exist in + /// the command to the SCSI unit. + /// + public uint Size + { + get + { + return 4 + 8; + } + } + + #region public flags + + /// + /// See SCSI MMC-4 specification + /// + public byte FOV + { + get + { + return (byte)((m_flags >> 7) & 0x01); + } + set + { + if (value != 0) + m_flags |= 0x80; + else + m_flags &= 0x7f; + } + } + + /// + /// See SCSI MMC-4 specification + /// + public byte DPRY + { + get + { + return (byte)((m_flags >> 6) & 0x01); + } + set + { + if (value != 0) + m_flags |= 0x40; + else + m_flags &= 0xbf; + } + } + + /// + /// See SCSI MMC-4 specification + /// + public byte DCRT + { + get + { + return (byte)((m_flags >> 5) & 0x01); + } + set + { + if (value != 0) + m_flags |= 0x20; + else + m_flags &= 0xdf; + } + } + + /// + /// See SCSI MMC-4 specification + /// + public byte STPF + { + get + { + return (byte)((m_flags >> 4) & 0x01); + } + set + { + if (value != 0) + m_flags |= 0x10; + else + m_flags &= 0xEF; + } + } + + /// + /// See SCSI MMC-4 specification + /// + public byte TRYOUT + { + get + { + return (byte)((m_flags >> 2) & 0x01); + } + set + { + if (value != 0) + m_flags |= 0x04; + else + m_flags &= 0xFB; + } + } + + /// + /// See SCSI MMC-4 specification + /// + public byte IMMED + { + get + { + return (byte)((m_flags >> 1) & 0x01); + } + set + { + if (value != 0) + m_flags |= 0x02; + else + m_flags &= 0xFD; + } + } + + /// + /// See SCSI MMC-4 specification + /// + public byte VS + { + get + { + return (byte)((m_flags >> 0) & 0x01); + } + set + { + if (value != 0) + m_flags |= 0x01; + else + m_flags &= 0xFE; + } + } + #endregion + + /// + /// Format the parameter list to the memory block to be sent down to the device. + /// The result should be a format parameter block as seen in the SCSI MMC specs. + /// + /// + public void FormatToMemory(IntPtr dest) + { + byte v; + int offset; + + Marshal.WriteByte(dest, 0, 0); // Byte 0 - reserved + + v = m_flags; + if (IP != null) + v |= 0x08; + Marshal.WriteByte(dest, 1, v); // Byte 1 - flags + + Marshal.WriteByte(dest, 2, 0); + Marshal.WriteByte(dest, 3, 8); // Format descriptor length = 8 + + offset = 4 ; + if (IP != null) + { + // If the initialization pattern exists, it goes here + throw new Exception("Not supported yet"); + } + + // Write the # of blocks + Marshal.WriteByte(dest, offset++, (byte)((NumberOfBlocks >> 24) & 0xff)); + Marshal.WriteByte(dest, offset++, (byte)((NumberOfBlocks >> 16) & 0xff)); + Marshal.WriteByte(dest, offset++, (byte)((NumberOfBlocks >> 8) & 0xff)); + Marshal.WriteByte(dest, offset++, (byte)((NumberOfBlocks >> 0) & 0xff)); + + byte b = (byte)(((byte)FormatType << 2) | FormatSubType) ; + Marshal.WriteByte(dest, offset++, b) ; + + Marshal.WriteByte(dest, offset++, (byte)((Parameter >> 16) & 0xff)); + Marshal.WriteByte(dest, offset++, (byte)((Parameter >> 8) & 0xff)); + Marshal.WriteByte(dest, offset++, (byte)((Parameter >> 0) & 0xff)); + } + } +} diff --git a/Bwg.Scsi/HeaderData.cs b/Bwg.Scsi/HeaderData.cs new file mode 100644 index 0000000..6b80a3a --- /dev/null +++ b/Bwg.Scsi/HeaderData.cs @@ -0,0 +1,142 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Scsi +{ + /// + /// This class contains the header data read from a sector + /// + public class HeaderData : Result + { + #region public types + /// + /// The block type, see SCSI MMC spec + /// + public enum BlockTypeType : byte + { + + /// + /// See SCSI MMC spec + /// + UserDataBlock = 0x00, + + /// + /// See SCSI MMC spec + /// + FourthRunInBlock = 0x01, + + /// + /// See SCSI MMC spec + /// + ThirdRunInBlock = 0x02, + + /// + /// See SCSI MMC spec + /// + SecondRunInBlock = 0x03, + + /// + /// See SCSI MMC spec + /// + FirstRunInBlock = 0x04, + + /// + /// See SCSI MMC spec + /// + LinkBlock = 0x05, + + /// + /// See SCSI MMC spec + /// + SecondRunOutBlock = 0x06, + + /// + /// See SCSI MMC spec + /// + FirstRunOutBlock = 0x07 + } ; + + /// + /// The data mode for the sector (only applies if track mode is 0x04) + /// + public enum DataTypeType : byte + { + /// + /// Mode 0 data, 2336 bytes of zero + /// + Mode0Data = 0x00, + + /// + /// Mode 1 data, 2048 bytes of user data + /// + Mode1Data = 0x01, + + /// + /// Mode 2 data, multiple forms of data + /// + Mode2Data = 0x02, + + /// + /// Reserved, should not be returned by a drive + /// + Reserved = 0x03 + } ; + #endregion + + #region public data members + /// + /// The time data read from the sector + /// + public readonly MinuteSecondFrame MSF; + + /// + /// The block type, see SCSI MMC spec + /// + public readonly BlockTypeType BlockType; + + /// + /// The data type, see SCSI MMC spec + /// + public readonly DataTypeType DataType; + #endregion + + /// + /// Create the header data object from a buffer containing the header data + /// + /// buffer containing the data + /// size of the data + public HeaderData(IntPtr buf, int size) + : base(buf, size) + { + byte b ; + + MSF = new MinuteSecondFrame(Get8(0), Get8(1), Get8(2)); + b = Get8(3); + BlockType = (BlockTypeType)((b >> 5) & 0x07); + DataType = (DataTypeType)(b & 0x03); + } + } +} diff --git a/Bwg.Scsi/InitializationPattern.cs b/Bwg.Scsi/InitializationPattern.cs new file mode 100644 index 0000000..433e7dd --- /dev/null +++ b/Bwg.Scsi/InitializationPattern.cs @@ -0,0 +1,77 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Scsi +{ + /// + /// The class representing a pattern to use in initialization the surface of a + /// DVD. + /// + public class InitializationPattern + { + /// + /// The type of modifier for the IP pattern + /// + public enum IPModifierType : byte + { + /// + /// + /// + NoHeader = 0, + + /// + /// + /// + WriteLBA = 1, + + /// + /// + /// + WriteLBAPhysical = 2, + + /// + /// + /// + Reserved = 3 + } + + /// + /// The modifier + /// + public IPModifierType IPModifier; + + /// + /// + /// + public byte PatternType; + + /// + /// + /// + public byte[] Pattern; + } +} diff --git a/Bwg.Scsi/InquiryResult.cs b/Bwg.Scsi/InquiryResult.cs new file mode 100644 index 0000000..fa82a79 --- /dev/null +++ b/Bwg.Scsi/InquiryResult.cs @@ -0,0 +1,128 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Scsi +{ + /// + /// This class contains the information returned from a SCSI Inquiry request + /// + public sealed class InquiryResult : Result + { + /// + /// The peripheral qualifier field from the SCSI inquiry reply, should be zero for MMC devices. + /// + public readonly byte PeripheralQualifier ; + + /// + /// The peripheral device type field from the SCSI inquiry reply, should be five for MMC devices. + /// + public readonly byte PeripheralDeviceType; + + /// + /// This gives the version number of the Inquiry data returned + /// + public readonly byte ResponseDataFormat; + + /// + /// The RMB field from the SCSI inquiry reply, should be true for MMC devices. + /// + public readonly bool RMB; + + /// + /// + /// + public readonly byte Version; + + /// + /// + /// + public readonly string VendorIdentification; + + /// + /// + /// + public readonly string ProductIdentification; + + /// + /// The version of the firmware + /// + public readonly string FirmwareVersion; + + /// + /// + /// + public readonly string ProductRevision; + + /// + /// This property returns true if the device supports removable medium. + /// + public bool RemovableMediumBit { get { return RMB; } } + + /// + /// + /// + /// + /// + public InquiryResult(IntPtr buffer, int size) : base(buffer, size) + { + ResponseDataFormat = (byte)(Get8(3) & 0x0f); + + // + // Make sure the inquiry data is in the right format + // A data format of 2 is good up through MMC-3. A data format of 3 + // is used for MMC-4 and MMC-5. Some drives (especially from LiteOn) + // use a value of 1, but still behave as expected. + // + if (ResponseDataFormat != 2 && ResponseDataFormat != 3 && ResponseDataFormat != 1) + { + m_valid = false; + return; + } + + PeripheralQualifier = (byte)((Get8(0) >> 5) & 0x07); + PeripheralDeviceType = (byte)(Get8(0) & 0x1f); + RMB = false; + if ((Get8(1) & 0x80) != 0) + RMB = true; + + Version = Get8(0x02); + + VendorIdentification = GetString(8, 15); + ProductIdentification = GetString(16, 31); + + int len = 35; + if (len >= size) + len = size - 1; + FirmwareVersion = GetString(32, len); + + if (size >= 56) + ProductRevision = GetString(36, 55); + else + ProductRevision = string.Empty; + } + } +} diff --git a/Bwg.Scsi/MinuteSecondFrame.cs b/Bwg.Scsi/MinuteSecondFrame.cs new file mode 100644 index 0000000..013c733 --- /dev/null +++ b/Bwg.Scsi/MinuteSecondFrame.cs @@ -0,0 +1,309 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; + +namespace Bwg.Scsi +{ + /// + /// This type represents a location on a disk in minute, seconds, frame format + /// + [Serializable()] + public class MinuteSecondFrame + { + /// + /// The number of 44100 Hz samples in a single frame + /// + public const long SamplesPerFrame = 588; + + /// + /// The number of frames in a second + /// + public const long FramesPerSecond = 75; + + /// + /// The number of seconds in a minute. + /// + public const long SecondsPerMinute = 60; + + /// + /// The minutes of this time frame + /// + public int Minutes; + + /// + /// The seconds of this time frame + /// + public byte Seconds; + + /// + /// The frames (at FramesPerSecond per second) of this time frame + /// + public byte Frames; + + /// + /// Constructor for the type + /// + /// + /// + /// + public MinuteSecondFrame(int m, int s, int f) + { + Minutes = m; + Seconds = (byte)s; + Frames = (byte)f; + } + + /// + /// Create a MinuteSecondFrame value from a sector count + /// + /// + public MinuteSecondFrame(long sectors) + { + long m = sectors / (FramesPerSecond * SecondsPerMinute); + sectors -= m * FramesPerSecond * SecondsPerMinute; + + long s = sectors / FramesPerSecond; + sectors -= s * FramesPerSecond; + + Debug.Assert(s < SecondsPerMinute); + Debug.Assert(sectors < FramesPerSecond); + + Minutes = (int)m; + Seconds = (byte)s; + Frames = (byte)sectors; + } + + /// + /// Return the sector count for this time span + /// + public long SectorCount + { + get + { + return Minutes * SecondsPerMinute * FramesPerSecond + Seconds * FramesPerSecond + Frames; + } + } + + /// + /// This property returns the logical block address associated with the time given. + /// + public long LogicalBlockAddress + { + get + { + long result = Minutes * FramesPerSecond * SecondsPerMinute + Seconds * FramesPerSecond + Frames; + + if (Minutes < 90) + result -= 150; + else + result -= 450150; + + return result; + } + } + + + /// + /// Return a hash code for this object + /// + /// hash value + public override int GetHashCode() + { + return base.GetHashCode(); + } + + /// + /// Returns true if the two objects are equal + /// + /// the object to compare to + /// true if the objects are equal, false otherwise + public override bool Equals(Object o) + { + if (o == null) + return false; + + if (!(o is MinuteSecondFrame)) + return false; + + + MinuteSecondFrame other = (MinuteSecondFrame)o; + return other.Minutes == Minutes && other.Seconds == Seconds && other.Frames == Frames; + } + + /// + /// Convert to a string + /// + public override string ToString() + { + return Minutes.ToString("") + ":" + Seconds.ToString("") + "." + Frames.ToString("") + ", " + SectorCount + " sectors"; + } + + /// + /// convert to a string with format options + /// + /// the format to use for the output + /// a string representing the object + public string ToString(string fmt) + { + string result = ""; + + if (fmt == "M:S") + { + result = Minutes.ToString("d2") + ":" + Seconds.ToString("d2"); + } + else if (fmt == "M:S.F") + { + result = Minutes.ToString("d2") + ":" + Seconds.ToString("d2") + "." + Frames.ToString("d2"); + } + else if (fmt == "M:S:F") + { + result = Minutes.ToString("d2") + ":" + Seconds.ToString("d2") + ":" + Frames.ToString("d2"); + } + else + { + throw new FormatException("the format '" + fmt + "' is not valid for the class Bwg.Scsi.MinuteSecondFrame"); + } + + return result; + } + + /// + /// Convert a number of samples into an MSF object + /// + /// the number of samples + /// + static public MinuteSecondFrame FromSampleCount(long samples) + { + long frames = samples / SamplesPerFrame; + uint m = (uint)(frames / (FramesPerSecond * SecondsPerMinute)); + frames -= m * FramesPerSecond * SecondsPerMinute; + + uint s = (uint)(frames / FramesPerSecond); + frames -= s * FramesPerSecond; + + Debug.Assert(s < SecondsPerMinute); + Debug.Assert(m < FramesPerSecond); + return new MinuteSecondFrame((byte)m, (byte)s, (byte)frames); + } + + /// + /// Operator for adding two MinuteSecondFrame times together + /// + /// the first operand + /// the second operand + /// the sum of t1 and t2 + static public MinuteSecondFrame operator +(MinuteSecondFrame t1, MinuteSecondFrame t2) + { + return new MinuteSecondFrame(t1.SectorCount + t2.SectorCount); + } + + /// + /// Operator for adding a time value with a frame count (or sector count) + /// + /// the time value + /// the count in sectors (or frames) + /// the addition time + static public MinuteSecondFrame operator +(MinuteSecondFrame t1, long t2) + { + return new MinuteSecondFrame(t1.SectorCount + t2); + } + + /// + /// Returns true if the first object is less than the second object + /// + /// the first object + /// the second object + /// true if t1 is less than t2 + static public bool operator <(MinuteSecondFrame t1, MinuteSecondFrame t2) + { + if (t1.Minutes < t2.Minutes) + return true; + + if (t1.Minutes > t2.Minutes) + return false; + + if (t1.Seconds < t2.Seconds) + return true; + + if (t1.Seconds > t2.Seconds) + return false; + + if (t1.Frames < t2.Frames) + return true; + + return false; + } + + /// + /// Returns true if the first object is greater than the second object + /// + /// the first object + /// the second object + /// true, if t1 is greater than t2 + static public bool operator >(MinuteSecondFrame t1, MinuteSecondFrame t2) + { + if (t1.Minutes > t2.Minutes) + return true; + + if (t1.Minutes < t2.Minutes) + return false; + + if (t1.Seconds > t2.Seconds) + return true; + + if (t1.Seconds < t2.Seconds) + return false; + + if (t1.Frames > t2.Frames) + return true; + + return false; + } + + /// + /// returns true if t1 is less than or equal to t2 + /// + /// + /// + /// + static public bool operator <=(MinuteSecondFrame t1, MinuteSecondFrame t2) + { + return (t1 < t2) || (t1 == t2); + } + + /// + /// + /// + /// + /// + /// + static public bool operator >=(MinuteSecondFrame t1, MinuteSecondFrame t2) + { + return (t1 > t2) || (t1 == t2); + } + } +} diff --git a/Bwg.Scsi/ModePage.cs b/Bwg.Scsi/ModePage.cs new file mode 100644 index 0000000..300e93a --- /dev/null +++ b/Bwg.Scsi/ModePage.cs @@ -0,0 +1,174 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; + +namespace Bwg.Scsi +{ + /// + /// This object represents a single mode page in a mode table. + /// + public class ModePage : Result + { + #region private member variables + /// + /// The data associated with the page table. This data also includes the page code and length + /// data elements from the mode page data. + /// + protected byte[] m_page_data; + + #endregion + + #region constructor + /// + /// This is the constructor for the mode page object. It builds a mode page object from + /// the raw reply area from the SCSI device. + /// + /// A pointer to the raw reply area + /// The size of the raw reply area in bytes + /// The offset to the mode page data + public ModePage(IntPtr buffer, int size, ref ushort offset) : base(buffer, size) + { + byte b = Get8(offset); + offset++; + byte len = Get8(offset); + offset++; + + m_page_data = new byte[len + 2]; + m_page_data[0] = b; + m_page_data[1] = len; + for(int i = 0 ; i < len ; i++) + m_page_data[i + 2] = Get8(offset++); + } + #endregion + + #region public properties + + /// + /// This is the data that makes up this mode page + /// + public byte[] PageData + { + get + { + return m_page_data; + } + } + + /// + /// The length of this mode page in bytes + /// + public ushort Length + { + get + { + return (ushort)m_page_data.GetLength(0); + } + } + + /// + /// The page code for this page + /// + public byte PageCode + { + get + { + return (byte)(m_page_data[0] & 0x3f); + } + } + + /// + /// If true, these parameters are saved permanently + /// + public bool ParametersSavable + { + get + { + return ((m_page_data[0] & 0x80) != 0) ? true : false; + } + + set + { + if (value) + m_page_data[0] |= 0x80; + else + m_page_data[0] &= 0x7f; + } + } + + #endregion + + #region public methods + /// + /// Retreive a 32 bit value from a mode page + /// + /// offset in the page to the value + /// a 32 bit value retreived from the mode page data + public int ModeGet32(int offset) + { + return m_page_data[offset + 0] << 24 | + m_page_data[offset + 1] << 16 | + m_page_data[offset + 2] << 8 | + m_page_data[offset + 3] << 0; + } + + /// + /// Set a 32 bit value in a mode page + /// + /// offset in the page to where the value will be placed + /// the value to write + public void ModeSet32(int offset, int value) + { + m_page_data[offset + 0] = (byte)((value >> 24) & 0xff); + m_page_data[offset + 1] = (byte)((value >> 16) & 0xff); + m_page_data[offset + 2] = (byte)((value >> 8) & 0xff); + m_page_data[offset + 3] = (byte)((value >> 0) & 0xff); + } + + /// + /// + /// + /// + /// + public ushort ModeGet16(int offset) + { + return (ushort)(m_page_data[offset + 0] << 8 | + m_page_data[offset + 1] << 0); + } + + /// + /// + /// + /// + /// + public void ModeSet16(int offset, ushort value) + { + m_page_data[offset + 0] = (byte)((value >> 8) & 0xff); + m_page_data[offset + 1] = (byte)((value >> 0) & 0xff); + } + #endregion + } +} diff --git a/Bwg.Scsi/ModeTable.cs b/Bwg.Scsi/ModeTable.cs new file mode 100644 index 0000000..725d3dc --- /dev/null +++ b/Bwg.Scsi/ModeTable.cs @@ -0,0 +1,108 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; + +namespace Bwg.Scsi +{ + /// + /// This class represents a mode table from the scsi device. + /// + public class ModeTable : Result + { + /// + /// This is the size of the header associated with a mode table. + /// + public const int ModeTableHeaderSize = 8; + + /// + /// The set of mode pages in this mode table. + /// + public IList Pages; + + /// + /// The constructor for the mode table object. It builds the mode table from + /// the raw reply buffer from the SCSI device. + /// + /// + /// + public ModeTable(IntPtr buffer, int size): base(buffer, size) + { + ushort len = Get16(0); + ushort index = ModeTableHeaderSize; + + Pages = new List(); + + while (index < len && index < size) + { + int b0 = Get8(index) & 0x3f; + + ModePage page ; + if (b0 == 0x05) + page = new WriteParameterModePage(buffer, size, ref index) ; + else + page = new ModePage(buffer, size, ref index); + + Pages.Add(page); + } + } + + /// + /// This property returns the total size of the raw mode table in bytes + /// + public ushort Size + { + get + { + ushort len = ModeTableHeaderSize; + + foreach (ModePage p in Pages) + len += p.Length; + + return len; + } + } + + /// + /// Format a mode table back into raw buffer for shipment down to a SCSI device. + /// + /// The buffer to receive the mode table + public void Format(IntPtr buffer) + { + ushort s = (ushort)(Size - ModeTableHeaderSize); + Marshal.WriteByte(buffer, 0, (byte)((s >> 8) & 0xff)); + Marshal.WriteByte(buffer, 1, (byte)(s & 0xff)); + + int offset = ModeTableHeaderSize; + foreach (ModePage p in Pages) + { + IntPtr dest = new IntPtr(buffer.ToInt32() + offset); + Marshal.Copy(p.PageData, 0, dest, p.Length); + offset += p.Length; + } + } + } +} diff --git a/Bwg.Scsi/OpcTableEntry.cs b/Bwg.Scsi/OpcTableEntry.cs new file mode 100644 index 0000000..183ee8e --- /dev/null +++ b/Bwg.Scsi/OpcTableEntry.cs @@ -0,0 +1,60 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Scsi +{ + /// + /// This class represents a single entry in the OPC table + /// + public class OpcTableEntry : Result + { + /// + /// The speed to which this entry applies + /// + public readonly ushort Speed; + + /// + /// The OPC table entries + /// + public readonly byte[] Table; + + /// + /// Constructor for an OPC table entry + /// + /// The raw buffer being processed + /// The size of the buffer + /// The offset into the buffer that contains the OPC entry + public OpcTableEntry(IntPtr buffer, int size, int offset) : base(buffer, size) + { + Speed = Get16(offset); + + Table = new byte [6] ; + for (int i = 0; i < 6; i++) + Table[i] = Get8(offset + i + 2); + } + } +} diff --git a/Bwg.Scsi/Performance.cs b/Bwg.Scsi/Performance.cs new file mode 100644 index 0000000..57bc280 --- /dev/null +++ b/Bwg.Scsi/Performance.cs @@ -0,0 +1,84 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Scsi +{ + /// + /// This class represents a performance entry from the device's performance list + /// + public class Performance : Result + { + /// + /// The start LBA for this entry + /// + public readonly uint StartLBA; + + /// + /// The starting performance for this entry + /// + public readonly uint StartPerformance; + + /// + /// The end LBA for this entry + /// + public readonly uint EndLBA; + + /// + /// The end performance for this entry + /// + public readonly uint EndPerformance; + + /// + /// The constructor to create a performance entry + /// + /// the buffer + /// the offset into the buffer + /// the size of the buffer + public Performance(IntPtr buffer, int offset, int size) + : base(buffer, size) + { + StartLBA = Get32(offset); + StartPerformance = Get32(offset + 4); + EndLBA = Get32(offset + 8); + EndPerformance = Get32(offset + 12); + } + + /// + /// Return a human readable string representing this object. + /// + /// + public override string ToString() + { + string str = string.Empty; + + str += "Start LBA=" + StartLBA.ToString(); + str += ", Start Perf=" + StartPerformance.ToString() + " Kb/sec"; + str += ", End LBA=" + EndLBA.ToString(); + str += ", End Perf=" + EndPerformance.ToString() + " Kb/sec"; + return str; + } + } +} diff --git a/Bwg.Scsi/PerformanceList.cs b/Bwg.Scsi/PerformanceList.cs new file mode 100644 index 0000000..cb50cb5 --- /dev/null +++ b/Bwg.Scsi/PerformanceList.cs @@ -0,0 +1,170 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// +using System; +using System.Collections.Generic; +using System.Collections; +using System.Text; +using System.Diagnostics; + +namespace Bwg.Scsi +{ + /// + /// A list of performance entries from the drive + /// + public class PerformanceList : Result, IEnumerable, IEnumerable + { + #region public types + /// + /// The type of the data (read or write) + /// + public enum DataType + { + /// + /// This means read data + /// + ReadData, + + /// + /// This means write data + /// + WriteData + } ; + + /// + /// + /// + public enum ExceptType : byte + { + /// + /// + /// + Nominal = 0, + + /// + /// + /// + Entire = 1, + + /// + /// + /// + Exceptions = 2 + } + + #endregion + + #region private data members + private DataType m_type; + private IList m_list; + private bool m_except; + #endregion + + /// + /// The constructor for the class + /// + /// + /// + public PerformanceList(IntPtr buffer, int size) + : base(buffer, size) + { + m_list = new List(); + + uint len = Get32(0); + int index = 8; + Debug.Assert(size >= 8); + + byte b = Get8(4); + if ((b & 0x02) != 0) + m_type = DataType.WriteData; + else + m_type = DataType.ReadData; + + if ((b & 0x01) != 0) + m_except = true; + else + m_except = false; + + while (index < len && index + 16 <= len && index + 16 <= size) + { + Performance p = new Performance(buffer, index, size); + m_list.Add(p); + + index += 16; + } + } + + /// + /// Return an indexed entry tino the performance list + /// + /// + /// + public Performance this[int index] + { + get + { + return m_list[index]; + } + } + + #region properties + /// + /// The type of the data + /// + public DataType Type + { + get + { + return m_type; + } + } + + /// + /// If true, this is exception data + /// + public bool IsExceptionData + { + get + { + return m_except; + } + } + #endregion + + #region public members + /// + /// Return an enumerator for this list + /// + /// + public IEnumerator GetEnumerator() + { + return m_list.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return m_list.GetEnumerator(); + } + + #endregion + } +} diff --git a/Bwg.Scsi/Properties/AssemblyInfo.cs b/Bwg.Scsi/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..40e7750 --- /dev/null +++ b/Bwg.Scsi/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Bwg.Scsi")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("BwgSoftware")] +[assembly: AssemblyProduct("BwgBurn")] +[assembly: AssemblyCopyright("Copyright © 2006 by Jack W. Griffin, Jr.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("d8330703-34ac-4124-a059-ba011391cc2e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("0.0.7.1")] +[assembly: AssemblyFileVersion("0.0.7.1")] diff --git a/Bwg.Scsi/Result.cs b/Bwg.Scsi/Result.cs new file mode 100644 index 0000000..6aedb54 --- /dev/null +++ b/Bwg.Scsi/Result.cs @@ -0,0 +1,205 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace Bwg.Scsi +{ + /// + /// This class is a base class for classes that contain results from SCSI commands. This + /// class contains information about whether or not that result is valid and provides functions + /// for extract data from the SCSI response. + /// + public abstract class Result + { + #region private data members + /// + /// The data buffer containing the data that makes up the result + /// + private IntPtr m_buffer ; + + /// + /// The size of the data in the buffer + /// + private int m_size ; + + /// + /// This member is true if the result is valid. It is false if an error occurred while + /// parsing the SCSI result. + /// + protected bool m_valid; + + #endregion + + #region constructor + /// + /// This is the constructor for the ScsiResult class. + /// + /// A pointer to the memory area containing the SCSI reply + /// The size of the memory area containing the SCSI reply + public Result(IntPtr buffer, int size) + { + m_valid = true; + m_buffer = buffer; + m_size = size; + } + #endregion + + #region public properties + /// + /// This property is true if the result is valid, and false otherwise. + /// + public bool Valid + { + get + { + return m_valid; + } + } + + /// + /// This property returns the size of the buffer + /// + public int BufferSize + { + get { return m_size; } + } + + /// + /// This property returns the buffer + /// + public IntPtr Buffer + { + get { return m_buffer; } + } + #endregion + + #region public member functions + /// + /// This method returns a byte of data from the memory area associated with the SCSI reply. + /// + /// The location of the byte to return within the reply + /// a byte of data from the reply + public byte Get8(int offset) + { + if (offset >= m_size) + throw new Exception("offset " + offset.ToString() + " is out side the range of the buffer"); + + return Marshal.ReadByte(m_buffer, offset); + } + + /// + /// + /// + /// + /// + public ushort Get16(int offset) + { + return (ushort)((Get8(offset) << 8) | Get8(offset + 1)); + } + + /// + /// Get a 16 bit number located in two different places in the buffer + /// + /// offset to the MSB of the number + /// offset to the LSB of the number + /// + public ushort Get16(int msb, int lsb) + { + return (ushort)((Get8(msb) << 8) | Get8(lsb)); + } + + /// + /// + /// + /// + /// + public uint Get24(int offset) + { + return (UInt32)((Get8(offset) << 16) | (Get8(offset + 1) << 8) | Get8(offset + 2)); + } + + /// + /// + /// + /// + /// + public uint Get32(int offset) + { + return (UInt32)((Get8(offset) << 24) | (Get8(offset + 1) << 16) | (Get8(offset + 2) << 8) | Get8(offset + 3)); + } + + /// + /// This method returns a 32 bit integer from an offset in the data buffer. The data is + /// ordered as big endian data per the SCSI-3 specification. + /// + /// the offset into the buffer + /// + public int Get32Int(int offset) + { + uint b = Get32(offset); + Debug.Assert(b < Int32.MaxValue); + return (int)b; + } + + /// + /// + /// + /// + /// + /// + public string GetString(int start, int stop) + { + string s = ""; + + for (int i = start; i <= stop; i++) + { + byte b = Get8(i); + s += (char)b; + } + + return s; + } + + /// + /// Return a true or false value based on a bit in the buffer + /// + /// offset to the byte containing the bit + /// the specific bit number + /// boolean reflecting the value of the bit + public bool GetBit(int offset, int bit) + { + byte b = Get8(offset); + + if ((b & (1 << bit)) != 0) + return true; + + return false; + } + #endregion + } +} diff --git a/Bwg.Scsi/SpeedDescriptor.cs b/Bwg.Scsi/SpeedDescriptor.cs new file mode 100644 index 0000000..1062113 --- /dev/null +++ b/Bwg.Scsi/SpeedDescriptor.cs @@ -0,0 +1,115 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Scsi +{ + /// + /// + /// + public class SpeedDescriptor : Result + { + /// + /// + /// + public readonly Device.RotationalControl WRC; + + /// + /// + /// + public readonly bool Exact; + + /// + /// + /// + public readonly bool MRW; + + /// + /// + /// + public readonly uint EndLBA; + + /// + /// + /// + public readonly int ReadSpeed; + + /// + /// + /// + public readonly int WriteSpeed; + + #region constructor + /// + /// + /// + /// + /// + /// + public SpeedDescriptor(IntPtr buffer, int offset, int size) + : base(buffer, size) + { + byte b = Get8(offset); + + if (((b & 0x03) >> 3 == 0)) + WRC = Device.RotationalControl.CLVandNonPureCav ; + else if (((b & 0x03) >> 3) == 1) + WRC = Device.RotationalControl.PureCav ; + + if ((b & 0x02) == 0) + Exact = false ; + else + Exact = true ; + + if ((b & 0x01) == 0) + MRW = false ; + else + MRW = true ; + + EndLBA = Get32(offset + 4); + ReadSpeed = Get32Int(offset + 8); + WriteSpeed = Get32Int(offset + 12); + } + + /// + /// Return a human readable string representing this object + /// + /// + public override string ToString() + { + string str = string.Empty; + + str += "WRC=" + WRC.ToString(); + str += ", Exact=" + Exact.ToString(); + str += ", MRW=" + MRW.ToString(); + str += ", EndLba=" + EndLBA.ToString(); + str += ", Read=" + ReadSpeed.ToString(); + str += ", Write=" + WriteSpeed.ToString(); + + return str; + } + #endregion + } +} diff --git a/Bwg.Scsi/SpeedDescriptorList.cs b/Bwg.Scsi/SpeedDescriptorList.cs new file mode 100644 index 0000000..0f3caff --- /dev/null +++ b/Bwg.Scsi/SpeedDescriptorList.cs @@ -0,0 +1,96 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// +using System; +using System.Collections.Generic; +using System.Collections; +using System.Text; +using System.Diagnostics; + +namespace Bwg.Scsi +{ + /// + /// + /// + public class SpeedDescriptorList : Result, IEnumerable, IEnumerable + { + private IList m_list; + + /// + /// + /// + /// + /// + public SpeedDescriptorList(IntPtr buffer, int size) + : base(buffer, size) + { + m_list = new List(); + + uint len = Get32(0) + 4; + int index = 8; + Debug.Assert(size >= 8); + + while (index < len && index < size) + { + SpeedDescriptor p = new SpeedDescriptor(buffer, index, size); + m_list.Add(p); + + index += 16; + } + } + + /// + /// This method is an indexor into the list of descriptors + /// + /// + /// + public SpeedDescriptor this[int index] + { + get + { + return m_list[index]; + } + } + + /// + /// Return the number of speed descriptors in the list + /// + public int Count + { + get { return m_list.Count; } + } + + /// + /// Return an enumerator for this list + /// + /// + public IEnumerator GetEnumerator() + { + return m_list.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return m_list.GetEnumerator(); + } + } +} diff --git a/Bwg.Scsi/SubheaderData.cs b/Bwg.Scsi/SubheaderData.cs new file mode 100644 index 0000000..9fad98b --- /dev/null +++ b/Bwg.Scsi/SubheaderData.cs @@ -0,0 +1,121 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Scsi +{ + /// + /// This class represents subheader data for a given sector + /// + public class SubheaderData : Result + { + #region public types + /// + /// The submode type + /// + [Flags] + public enum SubmodeType : byte + { + + /// + /// See SCSI MMC spec + /// + EndOfFile = 0x80, + + /// + /// See SCSI MMC spec + /// + RealTimeBlock = 0x40, + + /// + /// See SCSI MMC spec + /// + Form2 = 0x20, + + /// + /// See SCSI MMC spec + /// + TriggerBlock = 0x10, + + /// + /// See SCSI MMC spec + /// + DataBlock = 0x08, + + /// + /// See SCSI MMC spec + /// + AudioBlock = 0x04, // Not traditional CD-DA + + /// + /// See SCSI MMC spec + /// + VideoBlock = 0x02, + + /// + /// See SCSI MMC spec + /// + EndOfRecord = 0x01 + } ; + #endregion + + #region public data members + /// + /// File number, see SCSI MMC spec + /// + public readonly byte FileNumber; + + /// + /// Channel number, see SCSI MMC spec + /// + public readonly byte ChannelNumber; + + /// + /// Submode, see SCSI MMC spec + /// + public readonly SubmodeType Submode; + + /// + /// Coding information, see SCSI MMC spec + /// + public readonly byte CodingInformation; + + #endregion + + /// + /// Create subheader data information from subheader data read + /// + /// the buffer containing the subheader data + /// the size of the data + public SubheaderData(IntPtr buf, int size) + : base(buf, size) + { + FileNumber = Get8(0); + ChannelNumber = Get8(1); + Submode = (SubmodeType)Get8(2); + CodingInformation = Get8(3); + } + } +} diff --git a/Bwg.Scsi/TocEntry.cs b/Bwg.Scsi/TocEntry.cs new file mode 100644 index 0000000..86397d4 --- /dev/null +++ b/Bwg.Scsi/TocEntry.cs @@ -0,0 +1,80 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Scsi +{ + /// + /// A class representing a single TOC entry + /// + public class TocEntry : Result + { + /// + /// The ADR value for this track + /// + public byte Adr; + + /// + /// The control value for this track + /// + public byte Control; + + /// + /// The track number for this track + /// + public byte Number; + + /// + /// The start address (in sector) for this track + /// + public uint StartSector ; + + /// + /// The starting MSF for this track + /// + public MinuteSecondFrame StartMSF; + + /// + /// Create a new TOC entry + /// + /// the memory buffer holding the TOC + /// the offset to the TOC entry of interest + /// the overall size of the buffer + /// time versus lba mode + public TocEntry(IntPtr buffer, int offset, int size, bool mode) : base(buffer, size) + { + byte b = Get8(offset + 1) ; + Adr = (byte)((b >> 4) & 0x0f) ; + Control = (byte)(b & 0x0f) ; + Number = Get8(offset + 2) ; + + if (mode) + StartMSF = new MinuteSecondFrame(Get8(offset + 5), Get8(offset + 6), Get8(offset + 7)); + else + StartSector = Get32(offset + 4) ; + } + } +} diff --git a/Bwg.Scsi/TrackInformation.cs b/Bwg.Scsi/TrackInformation.cs new file mode 100644 index 0000000..213a55b --- /dev/null +++ b/Bwg.Scsi/TrackInformation.cs @@ -0,0 +1,204 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bwg.Scsi +{ + /// + /// This data structure contains the results from call ReadTrackInformation() to read + /// data about a track + /// + public class TrackInformation : Result + { + #region public types + /// + /// This type represents the data mode for the track + /// + public enum DataModeType + { + /// + /// Mode 1 user data (digital data, 2048 bytes per sector) + /// + Mode1 = 0x01, + + /// + /// Mode 2 user data (digital data, multiple forms) + /// + Mode2 = 0x02, + + /// + /// Audio data, 2352 bytes of audio data per sector + /// + Audio = 0x0f + } ; + #endregion + + #region public Track Information Members + /// + /// + /// + public readonly ushort TrackNumber; + + /// + /// + /// + public readonly ushort SessionNumber; + + /// + /// + /// + public readonly bool Copy; + + /// + /// + /// + public readonly bool Damage; + + /// + /// + /// + public readonly byte TrackMode; + + /// + /// + /// + public readonly bool RT; + + /// + /// + /// + public readonly bool Blank; + + /// + /// + /// + public readonly bool Packet; + + /// + /// + /// + public readonly bool FP; + + /// + /// + /// + public readonly DataModeType DataMode; + + /// + /// + /// + public readonly bool LRA_V; + + /// + /// + /// + public readonly bool NWA_V; + + /// + /// + /// + public readonly int TrackStartAddress; + + /// + /// + /// + public readonly int NextWritableAddress; + + /// + /// + /// + public readonly int FreeBlocks; + + /// + /// + /// + public readonly int FixedPacketSize; + + /// + /// + /// + public readonly int TrackSize; + + /// + /// + /// + public readonly int LastRecordedAddress; + + /// + /// + /// + public readonly int ReadCompatLBA; + + #endregion + + /// + /// Contruct a read track result object from the raw data. + /// + /// the raw data buffer + /// the size of the raw buffer in bytes + public TrackInformation(IntPtr buf, int size) : base(buf, size) + { + TrackNumber = Get16(32, 2) ; + SessionNumber = Get16(33, 3) ; + + Copy = GetBit(5,4) ; + Damage = GetBit(5,5) ; + TrackMode = (byte)(Get8(5) & 0x0f) ; + + RT = GetBit(6,7) ; + Blank = GetBit(6,6) ; + Packet = GetBit(6,5) ; + FP = GetBit(6,4) ; + DataMode = (DataModeType)(Get8(6) & 0x0f); + + LRA_V = GetBit(7, 1) ; + NWA_V = GetBit(7, 0) ; + + TrackStartAddress = (int)Get32(8) ; + NextWritableAddress = (int)Get32(12) ; + FreeBlocks = Get32Int(16) ; + FixedPacketSize = Get32Int(20) ; + TrackSize = Get32Int(24) ; + LastRecordedAddress = (int)Get32(28) ; + ReadCompatLBA = (int)Get32(36) ; + } + + #region public properties + + /// + /// An alias for fixed packet size depending on the mode of the track. + /// + public int BlockingFactor + { + get + { + return FixedPacketSize; + } + } + #endregion + } +} + diff --git a/Bwg.Scsi/WinDev.cs b/Bwg.Scsi/WinDev.cs new file mode 100644 index 0000000..1c0be7d --- /dev/null +++ b/Bwg.Scsi/WinDev.cs @@ -0,0 +1,190 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; + +namespace Bwg.Scsi +{ + /// + /// + /// + public unsafe class WinDev : IDisposable + { + // + // External functions required to interface to th + // + [DllImport("Kernel32.dll", SetLastError = true)] + private static extern IntPtr CreateFile(string h, uint acc, uint share, IntPtr sec, uint disp, uint flash, uint temp); + + [DllImport("Kernel32.dll", SetLastError = true)] + private static extern bool DeviceIoControl(IntPtr h, uint code, IntPtr inbuf, uint insize, IntPtr outbuf, uint outsize, ref uint returned, IntPtr Overlapped); + + [DllImport("Kernel32.dll", SetLastError = true)] + private static extern bool CloseHandle(IntPtr h); + + [DllImport("Kernel32.dll", SetLastError = false, CharSet = CharSet.Unicode)] + private static extern uint FormatMessage(uint flags, IntPtr src, uint errcode, uint langid, IntPtr str, uint size, IntPtr vargs) ; + + /// + /// The name of the device + /// + public string Name + { + get { CheckOpen() ; return m_name; } + } + + /// + /// Returns TRUE if the device is open, FALSE otherwise + /// + public bool IsOpen { get { return m_handle.ToInt32() != -1; } } + + private string m_name; + private IntPtr m_handle; + private uint m_last_error; + + /// + /// + /// + public WinDev() + { + m_handle = new IntPtr(-1); + } + /// + /// + /// + public uint LastError + { + get + { + return m_last_error; + } + } + + /// + /// + /// + public void Dispose() + { + if (IsOpen) + Close(); + } + + /// + /// + /// + public void Close() + { + CloseHandle(m_handle); + } + + /// + /// + /// + /// + /// + virtual public bool Open(string name) + { + string dname = name; + + // + // CreateFile + // name + // accessmode = GenericRead | GenericWrite + UInt32 acc = 0x80000000 | 0x40000000; + m_handle = CreateFile(dname, acc, 0x01, (IntPtr)0, 3, 0x00000080, (uint)0); + if (m_handle.ToInt32() == -1) + { + m_last_error = (uint)Marshal.GetLastWin32Error(); + return false; + } + + m_name = name; + return true; + } + + /// + /// + /// + /// + /// + virtual public bool Open(char letter) + { + string dname = "\\\\.\\" + letter + ":"; + return Open(dname); + } + + /// + /// + /// + protected void CheckOpen() + { + if (m_handle.ToInt32() == -1) + throw new Exception("device is not open") ; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + protected bool Control(uint code, IntPtr inbuf, uint insize, IntPtr outbuf, uint outsize, ref uint ret, IntPtr overlapped) + { + bool b; + + CheckOpen() ; + b = DeviceIoControl(m_handle, code, inbuf, insize, outbuf, outsize, ref ret, overlapped); + if (!b) + m_last_error = (uint)Marshal.GetLastWin32Error(); + + return b; + } + + /// + /// Convert a WIN32 error code to an error string. + /// + /// the error code to convert + /// the string for the error code + static public string Win32ErrorToString(uint error) + { + IntPtr buffer = Marshal.AllocHGlobal(1024) ; + uint ret = FormatMessage(0x1000, IntPtr.Zero, error, 0, buffer, 1024, IntPtr.Zero); + if (ret == 0) + return "cannot find win32 error string for this code (" + error.ToString() + ")"; + + string str = Marshal.PtrToStringUni(buffer); + str = str.Trim(); + Marshal.FreeHGlobal(buffer); + + return str; + } + } +} diff --git a/Bwg.Scsi/WriteBuffer.cs b/Bwg.Scsi/WriteBuffer.cs new file mode 100644 index 0000000..6c53a60 --- /dev/null +++ b/Bwg.Scsi/WriteBuffer.cs @@ -0,0 +1,137 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices ; + +namespace Bwg.Scsi +{ + /// + /// This class is used to buffer data to the SCSI device when + /// writing large amounts of data to the SCSI device. + /// + public class WriteBuffer : IDisposable + { + [DllImport("ntdll.dll")] + internal static extern void RtlZeroMemory(IntPtr dest, int size); + + [DllImport("ntdll.dll")] + internal static extern void RtlFillMemory(IntPtr dest, int size, byte value); + + #region public data members + + /// + /// The size of the buffer associated with this object (in sectors) + /// + public int BufferSize; + + /// + /// The amount of data stored in the buffer (in sectors) + /// + public int DataSize; + + /// + /// The size of a sector of data stored in this buffer page + /// + public int SectorSize; + + /// + /// This contains the logical block address for this block of data + /// + public long LogicalBlockAddress; + + /// + /// The source of this buffer, used only for debugging + /// + public string SourceString; + + #endregion + + #region private data members + + /// + /// The actual buffer memory + /// + private IntPtr m_buffer_ptr; + + #endregion + + #region public properties + + /// + /// Returns a buffer pointer to the internal buffer. + /// + public IntPtr BufferPtr + { + get + { + return m_buffer_ptr; + } + } + #endregion + + #region constructors + + /// + /// Create a new buffer object + /// + /// the size of the buffer + public WriteBuffer(int size) + { + m_buffer_ptr = Marshal.AllocHGlobal((int)size); + BufferSize = size; + LogicalBlockAddress = long.MaxValue; + } + #endregion + + #region public methods + /// + /// Free the memory associated with this buffer object + /// + public void Dispose() + { + Marshal.FreeHGlobal(m_buffer_ptr); + } + + /// + /// Fill the buffer with all zeros + /// + public void Zero() + { + RtlZeroMemory(m_buffer_ptr, BufferSize) ; + } + + /// + /// Fill the buffer with data of a specific byte. + /// + /// + public void Fill(byte b) + { + RtlFillMemory(m_buffer_ptr, BufferSize, b); + } + + #endregion + } +} diff --git a/Bwg.Scsi/WriteBufferPool.cs b/Bwg.Scsi/WriteBufferPool.cs new file mode 100644 index 0000000..c8cc9c6 --- /dev/null +++ b/Bwg.Scsi/WriteBufferPool.cs @@ -0,0 +1,284 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Diagnostics; + +namespace Bwg.Scsi +{ + /// + /// This class manages a series of buffers that are used to provide data + /// for the burning process. + /// + public class WriteBufferPool + { + #region private data members + int m_count ; + + private Object m_buffer_list_lock; + private IList m_buffer_list; + + private Object m_free_list_lock; + private IList m_free_list; + + private bool m_end_of_data; + + private int m_produced; + private int m_consumed; + + private int m_pagesize; + + private int m_mult; + private bool m_padded; + + #endregion + + #region public properties + /// + /// If this property is true, all data has been pushed into the + /// buffer stream. + /// + public bool EndOfData + { + get + { + return m_end_of_data; + } + set + { + m_end_of_data = value; + } + } + + /// + /// This property returns the number of 2k pages that have been produced by + /// the generator thread. + /// + public int Produced + { + get + { + return m_produced; + } + } + + /// + /// This property returns the number of 2k pages that have been consumed + /// by the writer thread. + /// + public int Consumed + { + get + { + return m_consumed; + } + } + + /// + /// This is the size of the pages stored in this buffer pool, in bytes. + /// + public int PageSize + { + get + { + return m_pagesize; + } + } + + /// + /// Return the percent of the buffers used + /// + public double PercentUsed + { + get + { + double v; + + lock (m_buffer_list_lock) + { + v = (double)m_buffer_list.Count / (double)m_count * 100.0; + } + return v; + } + } + + #endregion + + #region constructors + + /// + /// Initialize the buffer stream by creating the buffers + /// + /// the number of buffers to create + /// the size of each buffer + /// the multiple that each buffer must meet for the device + /// the size of the sectors in the buffer in bytes + public WriteBufferPool(int cnt, int size, int mult, int secsize) + { + m_count = cnt ; + m_end_of_data = false; + m_pagesize = size; + m_mult = mult; + + // + // If the multiple setting is non-zero, we may have to adjust the page size + // + if (m_mult != 0) + { + int blocks = size / (mult * secsize); + m_pagesize = blocks * mult * secsize; + } + + // Used to lock the data buffer list + m_buffer_list_lock = new Object(); + + // Used to lock the free buffer list + m_free_list_lock = new Object(); + + // The data buffer list + m_buffer_list = new List(); + + // THe free buffer list + m_free_list = new List() ; + + // Create the buffers and place them all in the + // free list. + for(int i = 0 ; i < cnt ; i++) + m_free_list.Add(new WriteBuffer(size)) ; + + m_produced = 0; + m_consumed = 0; + + // + // This starts out false and is only set to true if we have to pad + // a buffer to reach an appropriate multiple of the multiple count + // for this device. We should only have to pad the last buffer sent + // to the device. This value is used to detect data being supplied + // after having to pad a data buffer, implying that the data put into + // the queue did not full the buffer and was not at the end of the + // queue. + // + m_padded = false; + } + #endregion + + #region public methods + /// + /// Get a free write buffer to fill with data + /// + /// a write buffer + public WriteBuffer GetFreeBuffer() + { + while (true) + { + lock (m_free_list_lock) + { + if (m_free_list.Count > 0) + { + Debug.Assert(m_padded == false); + WriteBuffer buf = m_free_list[0] ; + m_free_list.RemoveAt(0) ; + return buf; + } + + if (m_end_of_data == true) + return null; + } + + // Sleep for 10 msec while we wait for buffers to become + // availsble. + Thread.Sleep(1) ; + } + } + + /// + /// Add a buffer to the list + /// + /// + public void SendBufferToDevice(WriteBuffer buf) + { + lock (m_buffer_list_lock) + { + if (m_mult != 0 && ((buf.DataSize % m_mult) != 0)) + System.Diagnostics.Debug.WriteLine("Added a block of " + buf.DataSize.ToString() + " blocks, not an event multiple") ; + + m_buffer_list.Add(buf); + m_produced += (int)buf.DataSize; + } + } + + /// + /// Get the next buffer of data from the steam + /// + /// the next buffer stream, or null if all data has been processed + public WriteBuffer GetNextDataBuffer() + { + while (true) + { + lock (m_buffer_list_lock) + { + if (m_buffer_list.Count > 0) + { + WriteBuffer buf = m_buffer_list[0]; + m_buffer_list.RemoveAt(0); + m_consumed += (int)buf.DataSize; + + if (m_mult != 0 && (buf.DataSize % m_mult) != 0) + { + // + // The buffer is not an even multiple of the + // + m_padded = true; + buf.DataSize = ((buf.DataSize / m_mult) + 1) * m_mult; + } + + return buf; + } + + if (m_end_of_data) + return null; + } + + // Wait for the reader thread to put data into the buffer + // stream + // Thread.Sleep(1); + } + } + + /// + /// Add a buffer back to the free list to be used again + /// + /// the buffer to add to the list + public void FreeWriteBuffer(WriteBuffer buf) + { + lock (m_free_list_lock) + { + m_free_list.Add(buf); + } + } + #endregion + } +} diff --git a/Bwg.Scsi/WriteBufferStream.cs b/Bwg.Scsi/WriteBufferStream.cs new file mode 100644 index 0000000..f15c00c --- /dev/null +++ b/Bwg.Scsi/WriteBufferStream.cs @@ -0,0 +1,267 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO ; +using System.Runtime.InteropServices; +using System.Diagnostics; + +namespace Bwg.Scsi +{ + /// + /// A stream that pushes the output data through the buffer pool to feed + /// the CD/DVD burner. + /// + public class WriteBufferStream: Stream + { + #region private data members + + /// + /// The current length of the stream in bytes + /// + private long m_length ; + + /// + /// The buffer pool, used to get buffers to write into and used to send + /// buffers to the device. + /// + private WriteBufferPool m_pool; + + /// + /// The current buffer we are writing into + /// + private WriteBuffer m_buffer; + + /// + /// The size of the sectors that will be sent to the device, in bytes + /// + private int m_sector_size; + + /// + /// The number of sectors that fit into a single write buffer + /// + private int m_sector_count; + + /// + /// The index into the current write buffer + /// + private int m_index; + + /// + /// If true, we are closing + /// + private bool m_closing; + + /// + /// The logical block address for the stream data + /// + private long m_lba; + + #endregion + + #region constructors + /// + /// This is a stream class that writes the data to the write buffer pool to be sent + /// down to the CD/DVD device. + /// + /// the buffer pool to get pages from and send data to + /// the size of the sectors to buffer + /// the logical block address for the first block from this stream + public WriteBufferStream(WriteBufferPool pool, int sector_size, long lba) + { + m_length = 0; + m_pool = pool; + m_buffer = null; + m_index = 0; + m_sector_size = sector_size; + m_sector_count = m_pool.PageSize / m_sector_size; + m_closing = false; + m_lba = lba; + } + #endregion + + #region public properties + /// + /// + /// + public override bool CanRead { get { return false; } } + + /// + /// + /// + public override bool CanSeek { get { return false; } } + + /// + /// + /// + public override bool CanWrite { get { return true ; } } + + /// + /// + /// + public override long Length { get { return m_length ; } } + + /// + /// + /// + public override long Position + { + get + { + return m_length; + } + set + { + throw new Exception("The method or operation is not implemented."); + } + } + + #endregion + + #region public methods + + /// + /// This method flushes any existing data to the device. + /// + public override void Flush() + { + if (m_buffer != null) + { + // + // In general using flush, except when closing the device can be very + // dangerous. This method catches those cases where the result sent to + // the device would be incorrect and asserts accordingly. Basically we + // always send data down to the device in sectors. When we flush we send + // down whatever data is left in the buffer. If we are closing, this has + // the effect of rounding the last bit of data to a sector boundary. If + // we are not closing, it can have the effect of inserting data in the + // middle of the data stream. Flusing only works correctly if we are sitting + // on the boundary of a sector. Therefore, if this assert has fired it means + // we are not closing and not sitting on the boundary of a sector. + // + Debug.Assert(m_closing || (m_index % m_sector_size) == 0); + + m_buffer.DataSize = m_index / m_sector_size; + if ((m_index % m_sector_size) != 0) + m_buffer.DataSize++; + m_buffer.SectorSize = m_sector_size; + + m_buffer.SourceString = "WriteBufferStream, Flush method"; + m_buffer.LogicalBlockAddress = m_lba; + m_lba = long.MaxValue; + m_pool.SendBufferToDevice(m_buffer); + } + } + + /// + /// + /// + /// + /// + /// + public override long Seek(long offset, SeekOrigin origin) + { + throw new Exception("The method or operation is not implemented."); + } + + /// + /// + /// + /// + /// + /// + /// + public override int Read(byte[] buffer, int offset, int count) + { + throw new Exception("The method or operation is not implemented."); + } + + /// + /// + /// + /// + /// + /// + public override void Write(byte[] buffer, int offset, int count) + { + while (count > 0) + { + if (m_buffer == null) + { + m_buffer = m_pool.GetFreeBuffer(); + m_index = 0; + } + + // + // Write up to the end of this buffer + // + int remaining = m_sector_count * m_sector_size - m_index; + + if (remaining > count) + remaining = count; + + IntPtr dest = new IntPtr(m_buffer.BufferPtr.ToInt32() + m_index); + Marshal.Copy(buffer, offset, dest, remaining); + m_index += remaining; + m_length += remaining; + count -= remaining ; + offset += remaining ; + + if (m_index == m_sector_count * m_sector_size) + { + m_buffer.DataSize = m_index / m_sector_size; + m_buffer.SectorSize = m_sector_size; + m_buffer.LogicalBlockAddress = m_lba; + m_buffer.SourceString = "WriteBufferStream, Write method"; + m_lba = long.MaxValue; + m_pool.SendBufferToDevice(m_buffer); + m_buffer = null; + } + } + } + + /// + /// + /// + /// + public override void SetLength(long value) + { + throw new Exception("The method or operation is not implemented."); + } + + /// + /// + /// + public override void Close() + { + m_closing = true; + + Flush(); + base.Close(); + } + + #endregion + } +} diff --git a/Bwg.Scsi/WriteParameterModePage.cs b/Bwg.Scsi/WriteParameterModePage.cs new file mode 100644 index 0000000..460ac6c --- /dev/null +++ b/Bwg.Scsi/WriteParameterModePage.cs @@ -0,0 +1,584 @@ +// +// BwgBurn - CD-R/CD-RW/DVD-R/DVD-RW burning program for Windows XP +// +// Copyright (C) 2006 by Jack W. Griffin (butchg@comcast.net) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the +// +// Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// +using System; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; + +namespace Bwg.Scsi +{ + /// + /// This class represents mode page 5, Write Parameters Mode Page + /// + public class WriteParameterModePage : ModePage + { + #region public types + /// + /// The type of write (packet/incremental, track, session) + /// + public enum WriteTypeType : byte + { + /// + /// Packet or incremental + /// + PacketIncremental = 0, + + /// + /// Track at once + /// + TrackAtOnce = 1, + + /// + /// Session at once + /// + SessionAtOnce = 2, + + /// + /// Raw data recording + /// + Raw = 3, + + /// + /// Layer jump recording + /// + LayerJumpRecording = 4 + } + + /// + /// The multisession state of the write page + /// + public enum MultiSessionType : byte + { + /// + /// No B0 pointer, therefore no next session allowed + /// + NoNextSession = 0, + + /// + /// B0 pointer equal to 0xFFFFFF, therefore not next session + /// allowed + /// + CDNoNextSessionFFFF = 1, + + /// + /// Reserved, do not use + /// + Reserved = 2, + + /// + /// B0 pointer points to next session + /// + NextSessionAllowed = 3 + } ; + + /// + /// + /// + public enum TrackModeType : byte + { + /// + /// + /// + TwoChannelAudio = 0x00, + + /// + /// + /// + TwoChannelAudioWithPreemphasis = 0x01, + + /// + /// + /// + DataUninterrupted = 0x04, + + /// + /// + /// + DataIncremental = 0x05, + + /// + /// + /// + FourChannelAudio = 0x08, + + /// + /// + /// + FourChannelAudioWithPreemphasis = 0x09, + } ; + + /// + /// + /// + public enum DataBlockTypeType : byte + { + /// + /// Raw data, 2352 bytes + /// + RawData = 0, + + /// + /// Raw data with P and Q subchannels, 2368 bytes + /// + RawDataWithPAndQ = 1, + + /// + /// Raw data with P - W subchannels packed, 2448 bytes + /// + RawDataWithPToWPacked = 2, + + /// + /// Raw data with P - W subchannels raw, 2448 bytes + /// + RawDataWithPToWRaw = 3, + + /// + /// Data mode 1, 2048 bytes + /// + DataMode1 = 8, + + /// + /// Data mode 2, 2336 bytes + /// + DataMode2 = 9, + + /// + /// Data mode 2, sub-header from write params, 2048 bytes + /// + DataMode2Form1 = 10, + + /// + /// Data mode 2, sub-header included, 2056 bytes + /// + DataMode2Form1Subheader = 11, + + /// + /// Data mode 2, form 2, sub-header from write params, 2324 bytes + /// + DataMode2Form2 = 12, + + /// + /// Data mode 2, form 2, sub-header included, 2332 bytes + /// + DataMode2Form2Subheader = 13, + } ; + + /// + /// + /// + public enum SessionFormatType : byte + { + /// + /// CD DA, CD ROM or other data disk + /// + CDDA_CDROM = 0x00, + + /// + /// CD-I disk + /// + CD_I = 0x10, + + /// + /// CD-ROM XA Disk + /// + CDROM_XA = 0x20, + } ; + + #endregion + + #region constructor + /// + /// Construct the mode page + /// + /// pointer to a buffer contains the mode page data + /// the size of the buffer area + /// the offset to the mode page + public WriteParameterModePage(IntPtr buffer, int size, ref ushort offset) + : base(buffer, size, ref offset) + { + } + #endregion + + #region public properties + /// + /// This property is the burn proof settings on this mode page + /// + public bool BurnProof + { + get + { + return (m_page_data[2] & 0x40) != 0; + } + set + { + if (value) + m_page_data[2] |= 0x40; + else + m_page_data[2] &= 0xbf; + } + } + + /// + /// If true, the link size field is valid. If not true, the link size is assumed to be + /// sever (per the SCSI MMC specification). + /// + public bool LinkSizeValid + { + get + { + return (m_page_data[2] & 0x20) != 0; + } + set + { + if (value) + m_page_data[2] |= 0x20; + else + m_page_data[2] &= 0xdf; + } + } + + /// + /// If true, any write will not effect the disk, and the write operation will only be a test. If + /// false the writes will go to the disk. This is also known as simulation. + /// + public bool TestWrite + { + get + { + return (m_page_data[2] & 0x10) != 0; + } + set + { + if (value) + m_page_data[2] |= 0x10; + else + m_page_data[2] &= 0xef; + } + } + + /// + /// This property sets the write type + /// + public WriteTypeType WriteType + { + get + { + return (WriteTypeType)(m_page_data[2] & 0x0f); + } + set + { + m_page_data[2] &= 0xf0; + m_page_data[2] |= (byte)value; + } + } + + /// + /// This property controls the multi-session mode of the session or track to + /// be burned. + /// + public MultiSessionType MultiSession + { + get + { + return (MultiSessionType)((m_page_data[3] >> 6) & 0x03); + } + set + { + Debug.Assert(value != MultiSessionType.Reserved); + + m_page_data[3] &= 0x3F; + m_page_data[3] |= (byte)((byte)value << 6); + } + } + + /// + /// This property controls whether the write mode is fixed packet. This only applies + /// if the write type is set to Packet/Incremental. + /// + public bool FixedPacket + { + get + { + return (m_page_data[3] & 0x20) != 0; + } + set + { + if (value) + m_page_data[3] |= 0x20; + else + m_page_data[3] &= 0xdf; + } + } + + /// + /// If true and the media is CD, SCMS copy protection is enabled. + /// + public bool Copy + { + get + { + return (m_page_data[3] & 0x10) != 0; + } + set + { + if (value) + m_page_data[3] |= 0x10; + else + m_page_data[3] &= 0xef; + } + } + + /// + /// The track mode. This should be 5 for DVD media, and is the + /// + public TrackModeType TrackMode + { + get + { + return (TrackModeType)(m_page_data[3] & 0x0d); + } + set + { + m_page_data[3] &= 0xf2; + m_page_data[3] |= (byte)value; + } + } + + /// + /// If true, digital copy if premitted of the content, otherwise it is not. + /// + public bool DigitalCopyPermitted + { + get + { + return (m_page_data[3] & 0x02) != 0; + } + set + { + if (value) + m_page_data[3] |= 0x02; + else + m_page_data[3] &= 0xfd; + } + } + + /// + /// + /// + public DataBlockTypeType DataBlockType + { + get + { + return (DataBlockTypeType)(m_page_data[4] & 0x0f); + } + set + { + m_page_data[4] &= 0xf0; + m_page_data[4] |= (byte)value; + } + } + + /// + /// + /// + public byte LinkSize + { + get + { + return m_page_data[5]; + } + set + { + m_page_data[5] = value; + } + } + + /// + /// + /// + public byte HostApplicationCode + { + get + { + return (byte)(m_page_data[7] & 0x3f); + } + set + { + m_page_data[7] &= 0xc0; + m_page_data[7] |= (byte)(value & 0x3f); + } + } + + /// + /// + /// + public SessionFormatType SessionFormat + { + get + { + return (SessionFormatType)m_page_data[8]; + } + set + { + m_page_data[8] = (byte)value; + } + } + + /// + /// + /// + public int PacketSize + { + get + { + return ModeGet32(10); + } + set + { + ModeSet32(10, value); + } + } + + /// + /// + /// + public ushort AudioPauseLength + { + get + { + return ModeGet16(14); + } + set + { + ModeSet16(14, value); + } + } + + /// + /// + /// + public byte[] MediaCatalogNumber + { + get + { + byte[] num = new byte[16]; + + for (int i = 0; i < 16; i++) + num[i] = m_page_data[16 + i]; + + return num; + } + set + { + Debug.Assert(value.GetLength(0) == 16); + + for (int i = 0; i < 16; i++) + m_page_data[16 + i] = value[i]; + } + } + + /// + /// + /// + public byte[] InternationalStandardRecordingCode + { + get + { + byte[] num = new byte[16]; + + for (int i = 0; i < 16; i++) + num[i] = m_page_data[32 + i]; + + return num; + } + set + { + Debug.Assert(value.GetLength(0) == 16); + + for (int i = 0; i < 16; i++) + m_page_data[32 + i] = value[i]; + } + } + + /// + /// + /// + public byte SubHeaderByte0 + { + get + { + return m_page_data[48]; + } + set + { + m_page_data[48] = value; + } + } + + /// + /// + /// + public byte SubHeaderByte1 + { + get + { + return m_page_data[49]; + } + set + { + m_page_data[49] = value; + } + } + + /// + /// + /// + public byte SubHeaderByte2 + { + get + { + return m_page_data[50]; + } + set + { + m_page_data[50] = value; + } + } + + /// + /// + /// + public byte SubHeaderByte3 + { + get + { + return m_page_data[51]; + } + set + { + m_page_data[51] = value; + } + } + #endregion + } +} diff --git a/CUETools.Ripper.Console/CUETools.ConsoleRipper.csproj b/CUETools.Ripper.Console/CUETools.ConsoleRipper.csproj new file mode 100644 index 0000000..4addc0c --- /dev/null +++ b/CUETools.Ripper.Console/CUETools.ConsoleRipper.csproj @@ -0,0 +1,101 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {9253A314-1821-42BF-B02F-2BF986B1765D} + Exe + Properties + CUETools.Ripper.Console + CUETools.Ripper.Console + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + ..\bin\x64\Debug\ + DEBUG;TRACE + full + x64 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + + + ..\bin\x64\Release\ + TRACE + true + pdbonly + x64 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + + + true + ..\bin\win32\Debug\ + DEBUG;TRACE + full + x86 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + + + ..\bin\win32\Release\ + TRACE + true + pdbonly + x86 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + + + + + + + + + + + + + {6458A13A-30EF-45A9-9D58-E5031B17BEE2} + AudioCodecsDotNet + + + {8CF07381-BEA2-4AFC-B3DD-9B2F21C65A3A} + CUETools.Ripper.SCSI + + + + + \ No newline at end of file diff --git a/CUETools.Ripper.Console/Program.cs b/CUETools.Ripper.Console/Program.cs new file mode 100644 index 0000000..9991efa --- /dev/null +++ b/CUETools.Ripper.Console/Program.cs @@ -0,0 +1,146 @@ +// **************************************************************************** +// +// CUERipper +// Copyright (C) 2008 Gregory S. Chudov (gchudov@gmail.com) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// **************************************************************************** + +using System; +using System.IO; +using System.Text; +using System.Collections.Generic; +using CUETools.Ripper.SCSI; +using AudioCodecsDotNet; + +namespace CUETools.ConsoleRipper +{ + class Program + { + static void Usage() + { + Console.WriteLine("Usage : CUETools.Ripper.Console.exe "); + Console.WriteLine(); + } + + static void Main(string[] args) + { + string programVersion = "CUERipper v1.9.3"; + Console.SetOut(Console.Error); + Console.WriteLine("{0}", programVersion); + Console.WriteLine("This is free software under the GNU GPLv3+ license; There is NO WARRANTY, to"); + Console.WriteLine("the extent permitted by law. for details."); + if (args.Length < 1) + { + Usage(); + return; + } + string destFile = args[0]; +#if !DEBUG + try +#endif + { + CDDriveReader audioSource = new CDDriveReader(); + audioSource.Open('D'); + + StreamWriter logWriter = new StreamWriter(Path.ChangeExtension(destFile, ".log")); + logWriter.WriteLine("{0}", programVersion); + logWriter.WriteLine(); + logWriter.WriteLine("Extraction logfile from {0}",DateTime.Now); + logWriter.WriteLine(); + logWriter.WriteLine("Used drive : {0}", audioSource.Path); + logWriter.WriteLine(); + logWriter.WriteLine("TOC of the extracted CD"); + logWriter.WriteLine(); + logWriter.WriteLine(" Track | Start | Length | Start sector | End sector"); + logWriter.WriteLine(" ---------------------------------------------------------"); + for (int track = 0; track < audioSource.TOC.tracks.Count; track++) + logWriter.WriteLine("{0,9} | {1,8} | {2,8} | {3,9} | {4,9}", + audioSource.TOC.tracks[track].Number, + audioSource.TOC.tracks[track].Start.MSF, + audioSource.TOC.tracks[track].Length.MSF, + audioSource.TOC.tracks[track].Start.Sector, + audioSource.TOC.tracks[track].End.Sector); + logWriter.Close(); + + //audioSource.Close(); + //return; + + bool toStdout = false; + WAVWriter audioDest = new WAVWriter(destFile, audioSource.BitsPerSample, audioSource.ChannelCount, audioSource.SampleRate, toStdout ? Console.OpenStandardOutput() : null); + int[,] buff = new int[audioSource.BestBlockSize, audioSource.ChannelCount]; + + Console.WriteLine("Filename : {0}", destFile); + Console.WriteLine("File Info : {0}kHz; {1} channel; {2} bit; {3}", audioSource.SampleRate, audioSource.ChannelCount, audioSource.BitsPerSample, TimeSpan.FromSeconds(audioSource.Length * 1.0 / audioSource.SampleRate)); + audioDest.FinalSampleCount = (long) audioSource.Length; + + DateTime start = DateTime.Now; + TimeSpan lastPrint = TimeSpan.FromMilliseconds(0); + + do + { + uint samplesRead = audioSource.Read(buff, Math.Min((uint)buff.GetLength(0), (uint)audioSource.Remaining)); + if (samplesRead == 0) break; + audioDest.Write(buff, samplesRead); + TimeSpan elapsed = DateTime.Now - start; + if ((elapsed - lastPrint).TotalMilliseconds > 60) + { + Console.Error.Write("\rProgress : {0:00}%; {1:0.00}x; {2}/{3}", + 100.0 * audioSource.Position / audioSource.Length, + audioSource.Position / elapsed.TotalSeconds / audioSource.SampleRate, + elapsed, + TimeSpan.FromMilliseconds(elapsed.TotalMilliseconds / audioSource.Position * audioSource.Length) + ); + lastPrint = elapsed; + } + } while (true); + + TimeSpan totalElapsed = DateTime.Now - start; + Console.Error.Write("\r \r"); + Console.WriteLine("Results : {0:0.00}x; {1}", + audioSource.Length / totalElapsed.TotalSeconds / audioSource.SampleRate, + totalElapsed + ); + audioDest.Close(); + + StreamWriter cueWriter = new StreamWriter(Path.ChangeExtension(destFile, ".cue")); + cueWriter.WriteLine("REM DISCID {0}", audioSource.TOC._cddbId); + cueWriter.WriteLine("REM ACCURATERIPID {0}", audioSource.TOC._ArId); + cueWriter.WriteLine("REM COMMENT \"{0}\"", programVersion); + if (audioSource.TOC._catalog != null) + cueWriter.WriteLine("CATALOG {0}", audioSource.TOC._catalog); + cueWriter.WriteLine("FILE \"{0}\" WAVE", destFile); + for (int track = 0; track < audioSource.TOC.tracks.Count; track++) + { + cueWriter.WriteLine(" TRACK {0:00} AUDIO", audioSource.TOC.tracks[track].Number); + for (int index = 0; index < audioSource.TOC.tracks[track].indexes.Count; index ++) + cueWriter.WriteLine(" INDEX {0:00} {1}", audioSource.TOC.tracks[track].indexes[index].Index, audioSource.TOC.tracks[track].indexes[index].MSF); + } + cueWriter.Close(); + + audioSource.Close(); + } +#if !DEBUG + catch (Exception ex) + { + Console.WriteLine(); + Console.WriteLine("Error: {0}", ex.Message); + Console.WriteLine("{0}", ex.StackTrace); + } +#endif + } + } +} diff --git a/CUETools.Ripper.Console/Properties/AssemblyInfo.cs b/CUETools.Ripper.Console/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5f4ca21 --- /dev/null +++ b/CUETools.Ripper.Console/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CUETools.Ripper.Console")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("CUETools.Ripper.Console")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2008")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2d93abce-7d63-4ac0-aa16-faef013f987e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CUETools.Ripper.SCSI/CUETools.Ripper.SCSI.csproj b/CUETools.Ripper.SCSI/CUETools.Ripper.SCSI.csproj new file mode 100644 index 0000000..522e01c --- /dev/null +++ b/CUETools.Ripper.SCSI/CUETools.Ripper.SCSI.csproj @@ -0,0 +1,109 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {8CF07381-BEA2-4AFC-B3DD-9B2F21C65A3A} + Library + Properties + CUETools.Ripper.SCSI + CUETools.Ripper.SCSI + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + ..\bin\x64\Debug\ + DEBUG;TRACE + full + x64 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + true + + + ..\bin\x64\Release\ + TRACE + true + pdbonly + x64 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + true + + + true + ..\bin\win32\Debug\ + DEBUG;TRACE + full + x86 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + true + + + ..\bin\win32\Release\ + TRACE + true + pdbonly + x86 + C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules + true + GlobalSuppressions.cs + prompt + true + + + + + + + + + + + + + {6458A13A-30EF-45A9-9D58-E5031B17BEE2} + AudioCodecsDotNet + + + {F2DFEB00-BB35-4665-85EA-CB8C7729A6B7} + Bwg.Logging + + + {A05B6AA6-0EC3-495D-BCC4-ECE1210071A8} + Bwg.Scsi + + + + + \ No newline at end of file diff --git a/CUETools.Ripper.SCSI/Properties/AssemblyInfo.cs b/CUETools.Ripper.SCSI/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c8211f8 --- /dev/null +++ b/CUETools.Ripper.SCSI/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CUETools.Ripper.SCSI")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("CUETools.Ripper.SCSI")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2008")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f80546ea-7f0c-4a5a-bcf3-fe53820a5bfe")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CUETools.Ripper.SCSI/SCSIDrive.cs b/CUETools.Ripper.SCSI/SCSIDrive.cs new file mode 100644 index 0000000..aca9544 --- /dev/null +++ b/CUETools.Ripper.SCSI/SCSIDrive.cs @@ -0,0 +1,452 @@ +// **************************************************************************** +// +// CUERipper +// Copyright (C) 2008 Gregory S. Chudov (gchudov@gmail.com) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// **************************************************************************** + +using System; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Text; +using Bwg.Scsi; +using Bwg.Logging; +using AudioCodecsDotNet; + +namespace CUETools.Ripper.SCSI +{ + /// + /// + /// + public class CDDriveReader : IAudioSource + { + byte[] cdtext = null; + private Device m_device; + uint _sampleOffset = 0; + uint _samplesInBuffer = 0; + uint _samplesBufferOffset = 0; + uint _samplesBufferSector = 0; + const int CB_AUDIO = 588 * 4 + 16; + const int NSECTORS = 32; + int _currentTrack = -1, _currentIndex = -1, _currentTrackActualStart = -1; + Logger m_logger = null; + CDImage _toc; + + public CDImage TOC + { + get + { + return _toc; + } + } + + public CDDriveReader() + { + } + + public bool Open(char Drive) + { + Device.CommandStatus st; + + // Open the base device + m_device = new Device(m_logger); + if (!m_device.Open(Drive)) + throw new Exception("SCSI error"); + + //// Open/Initialize the driver + //Drive m_drive = new Drive(dev); + //DiskOperationError status = m_drive.Initialize(); + //if (status != null) + // throw new Exception("SCSI error"); + + // { + //Drive.FeatureState readfeature = m_drive.GetFeatureState(Feature.FeatureType.CDRead); + //if (readfeature == Drive.FeatureState.Error || readfeature == Drive.FeatureState.NotPresent) + // throw new Exception("SCSI error"); + // }{ + //st = m_device.GetConfiguration(Device.GetConfigType.OneFeature, 0, out flist); + //if (st != Device.CommandStatus.Success) + // return CreateErrorObject(st, m_device); + + //Feature f = flist.Features[0]; + //ParseProfileList(f.Data); + // } + + SpeedDescriptorList speed_list; + st = m_device.GetSpeed(out speed_list); + if (st != Device.CommandStatus.Success) + throw new Exception("SCSI error"); + + st = m_device.SetCdSpeed(Device.RotationalControl.CLVandNonPureCav, Device.OptimumSpeed, Device.OptimumSpeed); + if (st != Device.CommandStatus.Success) + throw new Exception("SCSI error"); + + IList toc; + st = m_device.ReadToc((byte)0, false, out toc); + if (st != Device.CommandStatus.Success) + throw new Exception("SCSI error"); + + st = m_device.ReadCDText(out cdtext); + // new CDTextEncoderDecoder + + _toc = new CDImage(toc[toc.Count - 1].StartSector); + uint cddbDiscId = 0; + uint discId1 = 0; + uint discId2 = 0; + for (int iTrack = 0; iTrack < toc.Count - 1; iTrack++) + { + _toc.tracks.Add(new CDTrack((uint)iTrack + 1, toc[iTrack].StartSector, + toc[iTrack + 1].StartSector - toc[iTrack].StartSector)); + discId1 += toc[iTrack].StartSector; + discId2 += (toc[iTrack].StartSector == 0 ? 1 : toc[iTrack].StartSector) * ((uint)iTrack + 1); + cddbDiscId += sumDigits((uint)(toc[iTrack].StartSector / 75) + 2); + } + discId1 += toc[toc.Count - 1].StartSector; + discId2 += (toc[toc.Count - 1].StartSector == 0 ? 1 : toc[toc.Count - 1].StartSector) * ((uint)toc.Count); + discId1 &= 0xFFFFFFFF; + discId2 &= 0xFFFFFFFF; + cddbDiscId = (((cddbDiscId % 255) << 24) + + (((uint)(toc[toc.Count - 1].StartSector / 75) - (uint)(toc[0].StartSector / 75)) << 8) + + (uint)(toc.Count - 1)) & 0xFFFFFFFF; + _toc._cddbId = string.Format("{0:X8}", cddbDiscId); + _toc._ArId = string.Format("{0:x8}-{1:x8}-{2:x8}", discId1, discId2, cddbDiscId); + return true; + } + + public void Close() + { + _toc = null; + } + + public int BestBlockSize + { + get + { + return Math.Min(m_device.MaximumTransferLength / CB_AUDIO, NSECTORS) * 588; + } + } + + public unsafe uint Read(int[,] buff, uint sampleCount) + { + if (_toc == null) + throw new Exception("invalid TOC"); + if (_sampleOffset >= (uint)Length) + return 0; + if (_sampleOffset + sampleCount > Length) + sampleCount = (uint)Length - _sampleOffset; + uint pos = 0; + if (_samplesInBuffer > 0) + { + uint samplesRead = Math.Min(_samplesInBuffer, sampleCount); + AudioSamples.BytesToFLACSamples_16(_sectorBuffer, (int)(_samplesBufferSector * (588 * 4 + 16) + _samplesBufferOffset * 4), buff, (int)pos, samplesRead, 2); + pos += samplesRead; + sampleCount -= samplesRead; + _sampleOffset += samplesRead; + if (sampleCount == 0) + { + _samplesInBuffer -= samplesRead; + _samplesBufferOffset += samplesRead; + return pos; + } + _samplesInBuffer = 0; + _samplesBufferOffset = 0; + _samplesBufferSector = 0; + } + // if (_sampleOffset < PreGapLength && !_overreadIntoPreGap ... ? + int firstSector = (int)_sampleOffset / 588; + int lastSector = (int)(_sampleOffset + sampleCount + 577) / 588; + for (int sector = firstSector; sector < lastSector; sector += NSECTORS) + { + int Sectors2Read = ((sector + NSECTORS) < lastSector) ? NSECTORS : (lastSector - sector); + fixed (byte* data = _sectorBuffer) + { + Device.CommandStatus st = m_device.ReadCDAndSubChannel(2, 1, true, (uint)sector, (uint)Sectors2Read, (IntPtr)((void*)data), Sectors2Read * (2352 + 16)); + if (st != Device.CommandStatus.Success) + throw new Exception("SCSI error"); + } + for (int iSector = 0; iSector < Sectors2Read; iSector++) + { + uint samplesRead = Math.Min(sampleCount, 588U) - (_sampleOffset % 588); + AudioSamples.BytesToFLACSamples_16(_sectorBuffer, iSector * (588 * 4 + 16) + ((int)_sampleOffset % 588) * 4, buff, (int)pos, samplesRead, 2); + { + int q_pos = (iSector + 1) * (588 * 4 + 16) - 16; + int ctl = _sectorBuffer[q_pos + 0] >> 4; + int adr = _sectorBuffer[q_pos + 0] & 7; + bool preemph = (ctl == 1); + switch (adr) + { + case 1: // current position + { + int iTrack = fromBCD(_sectorBuffer[q_pos + 1]); + int iIndex = fromBCD(_sectorBuffer[q_pos + 2]); + if (iTrack != _currentTrack) + { + _currentTrack = iTrack; + _currentTrackActualStart = sector + iSector; + _currentIndex = iIndex; + if (_currentIndex == 1) + _toc.tracks[iTrack - 1].indexes.Add(new CDTrackIndex(1, _toc.tracks[iTrack - 1].Start.Sector)); + else if (_currentIndex != 0) + throw new Exception("invalid index"); + } + else + if (iIndex != _currentIndex) + { + if (iIndex != _currentIndex + 1) + throw new Exception("invalid index"); + _currentIndex = iIndex; + if (_currentIndex == 1) + { + int pregap = sector + iSector - _currentTrackActualStart; + _toc.tracks[iTrack - 1].indexes.Add(new CDTrackIndex(0, (uint)(_toc.tracks[iTrack - 1].Start.Sector - pregap))); + _currentTrackActualStart = sector + iSector; + } + _toc.tracks[iTrack - 1].indexes.Add(new CDTrackIndex((uint)iIndex, (uint)(_toc.tracks[iTrack - 1].Start.Sector + sector + iSector - _currentTrackActualStart))); + _currentIndex = iIndex; + } + break; + } + case 2: // catalog + if (_toc._catalog == null) + { + StringBuilder catalog = new StringBuilder(); + for (int i = 1; i < 8; i++) + catalog.AppendFormat("{0:x2}", _sectorBuffer[q_pos + i]); + _toc._catalog = catalog.ToString(0, 13); + } + break; + case 3: //isrc + break; + } + } + pos += samplesRead; + sampleCount -= samplesRead; + _sampleOffset += samplesRead; + if (sampleCount == 0) + { + _samplesBufferSector = (uint)iSector; + _samplesBufferOffset = samplesRead; + _samplesInBuffer = 588U - samplesRead; + return pos; + } + } + } + return pos; + } + + public ulong Length + { + get + { + if (_toc == null) + throw new Exception("invalid TOC"); + return (ulong)588 * _toc.Length.Sector; + } + } + + public int BitsPerSample + { + get + { + return 16; + } + } + + public int ChannelCount + { + get + { + return 2; + } + } + + public int SampleRate + { + get + { + return 44100; + } + } + + public NameValueCollection Tags + { + get + { + return null; + } + set + { + } + } + + public string Path + { + get + { + return m_device.Name; + } + } + + public ulong Position + { + get + { + return _sampleOffset; + } + set + { + _sampleOffset = (uint)value; + } + } + + public ulong Remaining + { + get + { + return Length - Position; + } + } + + byte[] _sectorBuffer = new byte[CB_AUDIO * NSECTORS]; + + private int fromBCD(byte hex) + { + return (hex >> 4) * 10 + (hex & 15); + } + + private uint sumDigits(uint n) + { + uint r = 0; + while (n > 0) + { + r = r + (n % 10); + n = n / 10; + } + return r; + } + } + + public class CDTrackIndex + { + public CDTrackIndex(uint index, uint sector) + { + _sector = sector; + _index = index; + } + + public uint Sector + { + get + { + return _sector; + } + } + + public uint Index + { + get + { + return _index; + } + } + + public string MSF + { + get + { + return new MinuteSecondFrame(_sector).ToString("M:S:F"); + } + } + + uint _sector; + uint _index; + } + + public class CDTrack + { + public CDTrack(uint number, uint start, uint length) + { + _number = number; + _start = start; + _length = length; + indexes = new List(); + } + + public CDTrackIndex Start + { + get + { + return new CDTrackIndex(0, _start); + } + } + + public CDTrackIndex Length + { + get + { + return new CDTrackIndex(0, _length); + } + } + + public CDTrackIndex End + { + get + { + return new CDTrackIndex(0, _start + _length - 1); + } + } + + public uint Number + { + get + { + return _number; + } + } + + public IList indexes; + + uint _start; + uint _length; + uint _number; + } + + public class CDImage + { + public CDImage(uint length) + { + tracks = new List(); + _length = length; + } + + public IList tracks; + + public CDTrackIndex Length + { + get + { + return new CDTrackIndex(0, _length); + } + } + + public string _catalog; + public string _cddbId; + public string _ArId; + uint _length; + } +}