singrdk/base/Drivers/Disk/ata6.cs

587 lines
26 KiB
C#
Raw Permalink Normal View History

2008-03-05 09:52:00 -05:00
////////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: ATA6.cs
//
// Base Classes and Interfaces for interacting with ATA
//
// References used:
// "AT Attachment with Packet Interface with Extension 6 (ATA/ATAPI-6-)",
// ISO Working Group T13, http://www.t13.org/
//
using System;
using System.Text;
using System.Threading;
using Microsoft.SingSharp;
using Microsoft.Singularity;
using Microsoft.Singularity.Io;
using Microsoft.Singularity.Drivers.IDE;
using Microsoft.Singularity.V1.Services;
using Microsoft.Contracts;
namespace Microsoft.Singularity.Drivers.IDE
{
[CLSCompliant(false)]
public class IdeDiskConfig
{
public IdeConfig ControllerConfig;
public byte DiskNumber;
public bool Atapi;
public String! DeviceName;
public String! Id;
public IdeDiskConfig(IdeConfig config, byte num, String! deviceName, bool atapi)
{
ControllerConfig = config;
DiskNumber = num;
Atapi = atapi;
Id = "/ata/disk";
DeviceName = deviceName;
// Used to be "/dev/disk0", but now we use the instance name.
base();
}
}
public class IdeConfig
{
public IoIrqRange Interrupt;
public bool UseDma;
public IoPortRange DmaBase;
public IoPortRange CommandBase;
public IoPortRange ControlBase;
public IdeController! IdeController;
public BusMasterDma BusMasterDma;
public IoIrq/*[!]*/ Irq;
public AutoResetEvent/*[!]*/ CommandEndEvent;
public AutoResetEvent/*[!]*/ IdentifyEndEvent;
public String Id;
public IdeConfig(IoIrqRange! interrupt,
IoPortRange! command,
IoPortRange! control,
bool useDma,
IoPortRange! bmBaseAddr,
[Delayed]
IdeController! ideController
)
{
Interrupt = interrupt;
CommandBase = command;
ControlBase = control;
UseDma = useDma;
DmaBase = bmBaseAddr;
IdeController = ideController;
Id = "/ata/controller";
Tracing.Log(Tracing.Debug,"IdeConfig: INT={1} cmd={2:x4}, ctrl={3:x4}, DMA@{4:x4}\n",
(UIntPtr)interrupt.Irq,
(UIntPtr)command.Port,
(UIntPtr)control.Port,
(UIntPtr)bmBaseAddr.Port);
}
}
[CLSCompliant(false)]
public struct IdentifyDeviceInformation
// Information Returned to host via the IdentifyDeviceInformation command
{
public bool RemovableMediaDevice; // Word 0, Bit 7
public bool RemovableController; // Word 0, Bit 6
public ushort CylinderCount; // word 1
public ushort HeadCount; // word 3
public ushort SectorCountPerTrack; // Word 6
public string SerialNumber; // Words 10 to 19
public ushort NumberOfBytesAvailInVendorCommand; // Word 22
public string FirmwareRevision; // Words 23 to 26
public string ModelNumber; // Words 27 to 47
public bool SupportsMultiple; // Word 47, bits 7 to 0 == 0
public ushort MaximumMultipleSectorCount; // Word 47, bits 7 to 0
public bool StandbyTimer; // Word 49, bit 13
public bool IORDYSupported; // Word 49, bit 11
public bool IORDYDisableable; // Word 49, bit 10
public bool LBASupported; // Word 49, bit 9
public bool DMASupported; // Word 49, bit 8
public bool Word88Valid; // Word 53, bit 2
public bool UltraDmaSupported; // Word 53, bit 2 (same)
public bool Words64to70Valid; // Word 53, bit 1
public bool Words54to58Valid; // Word 53, bit 0
// Words 54-58 are valid if word 53, bit 0 is set.
public ushort CurrentLogicalCylinderCount; // Word 54
public ushort CurrentLogicalHeadCount; // Word 55
public ushort CurrentLogicalSectorCount; // Word 56
public uint CurrentCapacityInSectors; // Word 57-58
public bool MultipleSectorSettingValid; // Word 59, bits 8
public ushort CurrentSectorCountPerInterrupt; // Word 59
public uint TotalAddressableSectors; // Word 60-61
public ushort MultiwordDMATransferMode; // Word 63, 15-8
public ushort MultiwordDMATransferModes; // Word 63, 7-0
// Words 64-70 are valid if word 53, bit 1 is set.
public ushort PIODMATransferModes; // Word 64, bits 7-0
public ushort MinimumMultiwordDMACycleTime; // in nanoseconds Word 65
public ushort RecommendedMultiwordDMACycleTime; // in nanoseconds Word 66
public ushort MinimumPIOCycleTimeWithFlowControl; // in nanoseconds Word 67
public ushort MinimumPIOCycleTimeWithIORDY; // in nanoseconds Word 68
public ushort QueueDepth; // word 75, value == depth-1
public ushort MajorVersion; //word 80
public ushort MinorVersion; //word 81
public ushort FeatureSet1Supported;
public bool NOPSupported; // word 82, bit 14
public bool ReadBufferSupported; // word 82, bit 13
public bool WriteBufferSupported; // word 82, bit 12
public bool HostProtectedAreaSupported; // word 82, bit 10
public bool DeviceResetSupported; // word 82, bit 9
public bool ServiceInterruptSupported; // word 82, bit 8
public bool ReleaseInterruptSupported; // word 82, bit 7
public bool LookAheadSupported; // word 82, bit 6
public bool WriteCacheSupported; // word 82, bit 5
public bool PacketSupported; // word 82, bit 4
public bool PowerManagementSupported; // word 82, bit 3
public bool RemovableMediaSupported; // word 82, bit 2
public bool SecurityModeSupported; // word 82, bit 1
public bool SmartSupported; // word 82, bit 0
public ushort FeatureSet2Supported;
public bool FlushCacheExtSupported; // word 83, bit 13
public bool FlushCacheSupported; // word 83, bit 12
public bool DeviceConfigurationOverlaySupported; // word 83, bit 11
public bool LBA48Supported; // word 83, bit 10
public bool AutomaticAcousticManagementSupported; // word 83, bit 9
public bool SetMaxSecuritySupported; // word 83, bit 8
public bool AddressOffsetrAreaBootSupported; // word 83, bit 7
public bool SetFeaturesSupported; // word 83, bit 6
public bool PowerUpInStandbySupported; // word 83, bit 5
public bool RemovableMediaStatusNotificationSupported; // word 83, bit 4
public bool APMSupported; // word 83, bit 3
public bool CFASupported; // word 83, bit 2
public bool QueuedDMASupported; // word 83, bit 1
public bool DownloadMicrocodeSupported; // word 83, bit 0
public ushort FeatureSet3Supported;
public bool GeneralPurposeLoggingSupported; // word 83, bit 5
public bool MediaCardPssThroughSupported; // word 83, bit 3
public bool MediaSerialNumberSupported; // word 83, bit 2
public bool SmartSelfTestSupported; // word 83, bit 1
public bool SmartErrorLogSupported; // word 83, bit 0
public bool EnabledDataValid; // word 87, bits 15,14 (0x8000)
public ushort FeatureSet1Enabled;
public bool NOPEnabled; // word 85, bit 14
public bool ReadBufferEnabled; // word 85, bit 13
public bool WriteBufferEnabled; // word 85, bit 12
public bool HostProtectedAreaEnabled; // word 85, bit 10
public bool DeviceResetEnabled; // word 85, bit 9
public bool ServiceInterruptEnabled; // word 85, bit 8
public bool ReleaseInterruptEnabled; // word 85, bit 7
public bool LookAheadEnabled; // word 85, bit 6
public bool WriteCacheEnabled; // word 85, bit 5
public bool PacketEnabled; // word 85, bit 4
public bool PowerManagementEnabled; // word 85, bit 3
public bool RemovableMediaEnabled; // word 85, bit 2
public bool SecurityModeEnabled; // word 85, bit 1
public bool SmartEnabled; // word 85, bit 0
public ushort FeatureSet2Enabled;
public bool FlushCacheExtEnabled; // word 86, bit 13
public bool FlushCacheEnabled; // word 86, bit 12
public bool DeviceConfigurationOverlayEnabled; // word 86, bit 11
public bool LBA48Enabled; // word 86, bit 10
public bool AutomaticAcousticManagementEnabled; // word 86, bit 9
public bool SetMaxSecurityEnabled; // word 86, bit 8
public bool AddressOffsetrAreaBootEnabled; // word 86, bit 7
public bool SetFeaturesEnabled; // word 86, bit 6
public bool PowerUpInStandbyEnabled; // word 86, bit 5
public bool RemovableMediaStatusNotificationEnabled; // word 86, bit 4
public bool APMEnabled; // word 86, bit 3
public bool CFAEnabled; // word 86, bit 2
public bool QueuedDMAEnabled; // word 86, bit 1
public bool DownloadMicrocodeEnabled; // word 86, bit 0
public ushort FeatureSet3Enabled;
public bool GeneralPurposeLoggingEnabled; // word 87, bit 5
public bool MediaCardPssThroughEnabled; // word 87, bit 3
public bool MediaSerialNumberEnabled; // word 87, bit 2
public bool SmartSelfTestEnabled; // word 87, bit 1
public bool SmartErrorLogEnabled; // word 87, bit 0
//word 88 UltraDMA
public ushort UltraDmaModes; //word 88
public byte HighestUltraDmaMode; // Highest UDMA mode supported;
public bool UltraDmaMode5Selected; //word 88. bit 13
public bool UltraDmaMode4Selected; //word 88. bit 12
public bool UltraDmaMode3Selected; //word 88. bit 11
public bool UltraDmaMode2Selected; //word 88. bit 10
public bool UltraDmaMode1Selected; //word 88. bit 9
public bool UltraDmaMode0Selected; //word 88. bit 8
public bool UltraDmaMode5Supported; //word 88. bit 5
public bool UltraDmaMode4Supported; //word 88. bit 4
public bool UltraDmaMode3Supported; //word 88. bit 3
public bool UltraDmaMode2Supported; //word 88. bit 2
public bool UltraDmaMode1Supported; //word 88. bit 1
public bool UltraDmaMode0Supported; //word 88. bit 0
public ulong MaxLBASectors; //words 100-103
}
#region ATA6
public class ATA6
{
internal static string WordsToString(ushort[]! DriveData, int Start, int End)
{
int iter;
int length = End - Start;
ASCIIEncoding xlate = new ASCIIEncoding();
byte[] tempBuffer = new byte[(End - Start) * 2];
2008-11-17 18:29:00 -05:00
for (iter = 0; iter < length; iter++) {
2008-03-05 09:52:00 -05:00
tempBuffer[iter * 2 + 1] = (byte)(DriveData[Start + iter] & (ushort)0xFF);
tempBuffer[iter * 2] = (byte)((DriveData[Start + iter] >> 8) & (ushort)0xFF);
}
string ret = xlate.GetString(tempBuffer);
if (ret != null) {
ret = ret.Trim();
}
return ret;
}
static byte HighestBitSet(ushort uiBits)
{
ushort uiMask = 0x80;
for (ushort nBit = 8; nBit > 0; nBit--, uiMask >>= 1) {
2008-11-17 18:29:00 -05:00
if ((uiBits & uiMask) > 0) {
2008-03-05 09:52:00 -05:00
return (byte)nBit;
}
}
return 0;
}
//public static uint MAX_SUPPORTED_SECTORS = 2048 * 128;
//public static uint MAX_SUPPORTED_SECTORS = 16383 * 16 * 63;
public static IdentifyDeviceInformation ParseIdentifyInformation(ushort[]! DriveData)
{
IdentifyDeviceInformation temp = new IdentifyDeviceInformation();
temp.RemovableMediaDevice = (DriveData[0] & (1<<7)) > 0;
temp.CylinderCount = DriveData[1];
temp.HeadCount = DriveData[3];
temp.SectorCountPerTrack = DriveData[6];
temp.SerialNumber = WordsToString(DriveData, 10, 19);
temp.NumberOfBytesAvailInVendorCommand = DriveData[22];
temp.FirmwareRevision = WordsToString(DriveData, 23, 26);
temp.ModelNumber = WordsToString(DriveData, 27, 46);
if ((DriveData[47] & (0xFF)) == 0) {
temp.SupportsMultiple = false;
}
else {
temp.SupportsMultiple = true;
temp.MaximumMultipleSectorCount = (ushort)(DriveData[47] & 0xFF);
}
temp.StandbyTimer = ((DriveData[49] & (1 << 13))) > 0;
temp.IORDYSupported = ((DriveData[49] & (1 << 11))) > 0;
temp.IORDYDisableable = ((DriveData[49] & (1 << 10))) > 0;
temp.LBASupported = ((DriveData[49] & (1 << 9))) > 0;
temp.DMASupported = ((DriveData[49] & (1 << 8))) > 0;
//UltraDMA supported
temp.Word88Valid = ((DriveData[53] & (1 << 2))) > 0;
temp.UltraDmaSupported = temp.Word88Valid;
//this bit should always be true unless device is CFA or PCMCIA
temp.Words64to70Valid = ((DriveData[53] & (1 << 1))) > 0;
//obsolete
temp.Words54to58Valid = ((DriveData[53] & (1 << 0))) > 0;
// word 59: multiple sector setting
temp.MultipleSectorSettingValid = (DriveData[59] & (1 << 8)) > 0;
temp.CurrentSectorCountPerInterrupt = (ushort)(DriveData[59] & 0xFF);
// words 60,61: Total # of addressable sectors (+1)
temp.TotalAddressableSectors = ((uint)DriveData[61] << 16) | (uint)DriveData[60];
#if obsolete
if (temp.TotalAddressableSectors > MAX_SUPPORTED_SECTORS) {
temp.TotalAddressableSectors = MAX_SUPPORTED_SECTORS;
}
#endif
// word 63: should be 0 if UltraDMA is supported
// bit 10: MDMA mode 2 selected
// bit 09: MDMA mode 1 selected
// bit 08: MDMA mode 0 selected
// bit 02: MDMA mode 2 supported
// bit 01: MDMA mode 1 supported
// bit 00: MDMA mode 0 supported
temp.MultiwordDMATransferMode = (ushort)((DriveData[63] & 0xFF00) >> 8);
temp.MultiwordDMATransferModes = (ushort)(DriveData[63] & 0xFF);
temp.PIODMATransferModes = (ushort)(DriveData[64] & 0xFF);
temp.MinimumMultiwordDMACycleTime = DriveData[65];
temp.RecommendedMultiwordDMACycleTime = DriveData[66];
temp.MinimumPIOCycleTimeWithFlowControl = DriveData[67];
temp.MinimumPIOCycleTimeWithIORDY = DriveData[68];
// value == maximum queue depth -1 (max = 31 +1)
temp.QueueDepth = (ushort)(DriveData[75] & 0x1F);
temp.MajorVersion = DriveData[80];
temp.MinorVersion = DriveData[81];
//Feature sets
temp.FeatureSet1Supported = DriveData[82];
temp.FeatureSet2Supported = DriveData[83];
temp.FeatureSet3Supported = DriveData[84];
temp.FeatureSet1Enabled = DriveData[85];
temp.FeatureSet2Enabled = DriveData[86];
temp.FeatureSet3Enabled = DriveData[87];
temp.LBA48Supported = ((DriveData[83] & (1 << 10))) > 0;
temp.LBA48Enabled = ((DriveData[86] & (1 << 10))) > 0;
temp.QueuedDMASupported = ((DriveData[83] & (1 << 1))) > 0;
temp.QueuedDMAEnabled = ((DriveData[86] & (1 << 1))) > 0;
if (temp.UltraDmaSupported) {
temp.UltraDmaModes = DriveData[88];
temp.HighestUltraDmaMode =
(byte) (HighestBitSet((ushort)(temp.UltraDmaModes & 0x3f)) - (byte) 1);
}
if (temp.LBA48Supported) {
temp.MaxLBASectors = ((ulong)DriveData[103] << 48)
| ((ulong)DriveData[102] << 32)
| ((ulong)DriveData[101] << 16)
| (ulong)DriveData[100];
DebugStub.WriteLine("Disk Device supports LBA 48\n");
DebugStub.WriteLine("Total LBA sectors ={0}",__arglist(temp.MaxLBASectors));
//controller.WriteCommandPort( (byte) ATA6.IdeCommand.ReadNativeMaxAddressExt);
//controller.Delay400ns();
//DebugStub.WriteLine("ReadNativeMaxAddressExt:");
//ShowOffsetAndCount();
}
else {
temp.MaxLBASectors = temp.TotalAddressableSectors;
}
if (temp.UltraDmaSupported) {
DebugStub.WriteLine("Disk Device supports Ultra DMA modes={0:x4},highest={1}",
__arglist(temp.UltraDmaModes,temp.HighestUltraDmaMode));
}
return temp;
}
public static
IdentifyDeviceInformation GetDeviceIdentification(IdeController! controller,
int devsel, bool atapi)
{
ushort[] identifyData = new ushort[256];
if (!controller.PollBSY(false)) {
// should fail here.
}
controller.SelectDevice(devsel, atapi);
//DebugStub.Print(" IDE GetDeviceIdentification starting\n");
2008-11-17 18:29:00 -05:00
bool iflag = PrivilegedGate.DisableInterrupts();
2008-03-05 09:52:00 -05:00
try {
controller.SetIdentifyInProgress(true);
controller.PollDRDY(true);
byte cmd = (atapi)? (byte)IdeCommand.IdentifyPacketDevice:
(byte)IdeCommand.IdentifyDevice;
controller.WriteCommandPort(cmd); //will post interrupt
controller.Delay400ns();
Tracing.Log(Tracing.Debug,"status={0}",(UIntPtr) controller.ReadFullStatus());
controller.PollDRDY(true); // Device indicates ready.
}
finally {
2008-11-17 18:29:00 -05:00
PrivilegedGate.RestoreInterrupts(iflag);
2008-03-05 09:52:00 -05:00
}
IdeConfig config = controller.GetConfig();
bool success = (config != null) && config.IdentifyEndEvent.WaitOne(TimeSpan.FromMilliseconds(250));
if (!success) {
//DebugStub.Break();
Tracing.Log(Tracing.Debug," IDE GetDeviceIdentification did NOT receive interrupt\n");
controller.SetIdentifyInProgress(false);
}
//DebugStub.Print(" IDE GetDeviceIdentification received interrupt\n");
// XXX: We should check for an Error condition here.
// XXX: We should check for the ATAPI signature here.
byte statusBits = controller.ReadAlternateStatusPort();
if ((statusBits & (byte)ATA6.IdeStatus.ERR) > 0) {
Tracing.Log(Tracing.Debug,"ERR reported in status after IdentifyDevice!\n");
DebugStub.Break();
}
for (int i = 0; i < 256; i++) {
identifyData[i] = controller.ReadDataPort();
}
IdentifyDeviceInformation ident = ParseIdentifyInformation(identifyData);
//// if (atapi)
if (ident.ModelNumber == "Virtual CD") // SECTOR_SIZE is actually 2048
ident.MaxLBASectors = 650 * 1024 * 1024 / IdeRequest.SECTOR_SIZE;
// for IdeDisk::ReadWrite tests
return ident;
}
public static void DebugQueryStatus(IdeController! controller)
{
byte statusBits = controller.ReadAlternateStatusPort();
byte errorBits = controller.ReadErrorPort();
Tracing.Log(Tracing.Debug,"ATAcontroller: Status Register({0:x2}): ",statusBits);
Tracing.Log(Tracing.Debug, ((statusBits & (byte) ATA6.IdeStatus.BSY) > 0)
? "BSY " : "!bsy ");
Tracing.Log(Tracing.Debug, ((statusBits & (byte)ATA6.IdeStatus.DRDY) > 0)
? "DRDY " : "!drdy ");
Tracing.Log(Tracing.Debug, ((statusBits & (byte)ATA6.IdeStatus.DWF) > 0)
? "DWF " : "!dwf ");
Tracing.Log(Tracing.Debug, ((statusBits & (byte)ATA6.IdeStatus.DSC) > 0)
? "DSC " : "!dsc ");
Tracing.Log(Tracing.Debug, ((statusBits & (byte)ATA6.IdeStatus.DRQ) > 0)
? "DRQ " : "!drq ");
Tracing.Log(Tracing.Debug, ((statusBits & (byte)ATA6.IdeStatus.CORR) > 0)
? "CORR " : "!corr ");
Tracing.Log(Tracing.Debug, ((statusBits & (byte)ATA6.IdeStatus.IDX) > 0)
? "IDX " : "!idx");
Tracing.Log(Tracing.Debug, ((statusBits & (byte)ATA6.IdeStatus.ERR) > 0)
? "ERR " : "!err ");
Tracing.Log(Tracing.Debug,"\n");
Tracing.Log(Tracing.Debug,"ATAcontroller: Error Register:({0:x2}): ",errorBits);
Tracing.Log(Tracing.Debug, ((errorBits & (byte)ATA6.IdeError.UNC) > 0)
? "UNC " : "!unc ");
Tracing.Log(Tracing.Debug, ((errorBits & (byte)ATA6.IdeError.MC) > 0)
? "MC " : "!mc ");
Tracing.Log(Tracing.Debug, ((errorBits & (byte)ATA6.IdeError.IDNF) > 0)
? "IDNF " : "!idnf ");
Tracing.Log(Tracing.Debug, ((errorBits & (byte)ATA6.IdeError.ABRT) > 0)
? "ABRT " : "!abrt ");
Tracing.Log(Tracing.Debug, ((errorBits & (byte)ATA6.IdeError.MCR) > 0)
? "MCR " : "!mcr ");
Tracing.Log(Tracing.Debug, ((errorBits & (byte)ATA6.IdeError.TK0NF) > 0)
? "TK0NF " : "!tk0nf ");
Tracing.Log(Tracing.Debug, ((errorBits & (byte)ATA6.IdeError.AMNF) > 0)
? "AMNF " : "!amnf ");
Tracing.Log(Tracing.Debug,"\n");
ushort lbaLow = controller.ReadLBALowPort();
ushort lbaMid = controller.ReadLBAMidPort();
ushort lbaHigh = controller.ReadLBAHighPort();
Tracing.Log(Tracing.Debug,"LBA={0:x2},{1:x2},{2:x2}\n",
(UIntPtr)lbaHigh, (UIntPtr)lbaMid, (UIntPtr)lbaLow);
}
public enum IdeCommand
{
Nop = 0x00,
Read = 0x20,
ReadNoRetry = 0x21,
ReadLong = 0x22,
ReadLongNoRetry = 0x23,
ReadDmaExt = 0x25,
ReadDmaQueuedExt = 0x26,
ReadNativeMaxAddressExt = 0x27,
Write = 0x30,
WriteNoRetry = 0x31,
WriteLong = 0x32,
WriteLongNoRetry = 0x33,
WriteDmaExt = 0x35,
WriteDmaQueuedExt = 0x36,
WriteVerify = 0x3C,
ReadVerify = 0x40,
ReadVerifyNoRetry = 0x41,
FormatTrack = 0x50,
Seek = 0x70,
ExecuteDeviceDiagnostic = 0x90,
InitializeDeviceParameters = 0x91,
DownloadMicrocode = 0x92,
AtapiPacket = 0xA0,
IdentifyPacketDevice = 0xA1,
ReadMultiple = 0xC4,
WriteMultiple = 0xC5,
SetMultipleMode = 0xC6,
ReadDmaQueued = 0xC7,
ReadDmaRetry = 0xC8,
ReadDmaNoRetry = 0xC9,
WriteDmaRetry = 0xCA,
WriteDmaNoRetry = 0xCB,
WriteDmaQueued = 0xCC,
AcknowledgeMediaChange = 0xDB,
BootPostBoot = 0xDC,
BootPreBoot = 0xDD,
DoorLock = 0xDE,
DoorUnLock = 0xDF,
ReadBuffer = 0xE4,
WriteBuffer = 0xE8,
WriteSame = 0xE9,
IdentifyDevice = 0xEC,
MediaEject = 0xED,
SetFeature = 0xEF,
}
public enum Atapi : byte
{
DMA = 0x01,
Read = 0x28,
}
public enum IdeStatus : byte
{
BSY = 0x80,
DRDY = 0x40,
DWF = 0x20,
DSC = 0x10,
DRQ = 0x08,
CORR = 0x04,
IDX = 0x02,
ERR = 0x01,
}
public enum IdeError
{
UNC = 0x40,
MC = 0x20,
IDNF = 0x10,
ABRT = 0x08,
MCR = 0x04,
TK0NF = 0x02,
AMNF = 0x01,
}
public const byte DriveSelect = 0x10; // Low = Master, High = Slave
public const bool High = true;
public const bool Low = false;
public const byte DeviceControlSRST = 0x04;
public const byte DeviceControlnIEN = 0x02; // note active low
// High Order Bit, used to read LBA 48 "previous" values in low,mid,and high port queues
public const byte DeviceControlHOB = 0x80;
}
#endregion
}