using System; using System.Collections.Generic; using System.Linq; using System.Text; using Cosmos.Hardware; using Cosmos.Kernel; namespace MatthijsTest { public class MyATAController : Device { private const ushort PrimaryControllerBaseAddress = 0x1F0; public static void Scan() { var xController = new MyATAController(PrimaryControllerBaseAddress); xController.Identify(true); xController.Identify(false); Device.Devices.Add(xController); } private void Identify(bool masterDrive) { // Select the drive to identify DriveSelectPort = (byte)(masterDrive ? 0xA0 : 0xB0); // read status var xStatus = StatusPort; xStatus = StatusPort; xStatus = StatusPort; xStatus = StatusPort; xStatus = StatusPort; CommandPort = CommandEnum.Identify; if (StatusPort == RegularStatusFlagsEnum.None) { return; } var xError = false; var xReady = false; while (true) { xStatus = StatusPort; if (xStatus.HasFlags(RegularStatusFlagsEnum.Error)) { xError = true; break; } if (xStatus.HasFlags(RegularStatusFlagsEnum.DataRequest) & !xStatus.HasFlags(RegularStatusFlagsEnum.Busy)) { xReady = true; break; } } if (xError) { // preliminary abort, means it's ATAPI or SATA // todo: implement support for SATA and ATAPI detection return; } if (!xReady) { Console.WriteLine("Weird error. Situation not handled in ATAController.Identify!"); while (true) ; } // not busy anymore. var xName = masterDrive ? "Master drive" : "Slave drive"; var xDevice = new MyATADevice(this, xName, masterDrive); Devices.Add(xDevice); if (masterDrive) { HasMasterDevice = true; } else { HasSlaveDevice = true; } } private MyATAController(ushort baseAddress) { mType = DeviceType.Storage; mAddressSpace = new IOAddressSpace(baseAddress, 8); } private IOAddressSpace mAddressSpace; #region IO Ports private ushort DataPort_Word { get { return mAddressSpace.Read16(0); } set { mAddressSpace.Write16(0, value); } } private byte SectorCountPort { set { mAddressSpace.Write8(2, value); } } private byte SectorAddress1Port { set { mAddressSpace.Write8(3, value); } } private byte SectorAddress2Port { set { mAddressSpace.Write8(4, value); } } private byte SectorAddress3Port { set { mAddressSpace.Write8(5, value); } } private RegularStatusFlagsEnum StatusPort { get { return (RegularStatusFlagsEnum)mAddressSpace.Read8(7); } } private CommandEnum CommandPort { set { mAddressSpace.Write8(7, (byte)value); } } private byte DriveSelectPort { get { return mAddressSpace.Read8(6); } set { mAddressSpace.Write8(6, value); } } #endregion public bool HasMasterDevice { get; private set; } public bool HasSlaveDevice { get; private set; } public override string Name { get { return "ATA Storage Controller"; } } #region PIO internal void RealReadBlock_PIO(AddressSpace target) { for (uint i = 0; i < 256; i++) { target.Write16(i * 2, DataPort_Word); } } internal void RealWriteBlock_PIO(byte[] source) { for (uint i = 0; i < 256; i++) { DataPort_Word = (ushort)(source[i *2] | (source[(i*2)+1] << 8)); } } internal void RealWriteBlock_PIO(AddressSpace source) { for (uint i = 0; i < 256; i++) { DataPort_Word = source.Read16Unchecked(i * 2); } } internal void ReadSector_LBA28(bool masterDrive, uint block, AddressSpace target) { var xDriveSelect = (byte)0xE0; if (!masterDrive) { xDriveSelect = 0xF0; } DriveSelectPort = (byte)(xDriveSelect | ((block >> 24) & 0xF)); SectorCountPort = 1; SectorAddress1Port = (byte)block; SectorAddress2Port = (byte)(block >> 8); SectorAddress3Port = (byte)(block >> 16); CommandPort = CommandEnum.ReadSectors; bool xPoll = true; bool xError = false; do { var xStatus = StatusPort; if ((!xStatus.HasFlags(RegularStatusFlagsEnum.Busy)) && xStatus.HasFlags(RegularStatusFlagsEnum.DataRequest)) { xPoll = false; } if (xStatus.HasFlags(RegularStatusFlagsEnum.Error) || xStatus.HasFlags(RegularStatusFlagsEnum.DriveFault)) { xPoll = false; xError = true; } } while (xPoll); if (xError) { throw new Exception("Error while reading sector!"); } RealReadBlock_PIO(target); } internal void WriteSector_LBA28(bool masterDrive, uint block, byte[] source) { var xDriveSelect = (byte)0xE0; if (!masterDrive) { xDriveSelect = 0xF0; } DriveSelectPort = (byte)(xDriveSelect | ((block >> 24) & 0xF)); SectorCountPort = 1; SectorAddress1Port = (byte)block; SectorAddress2Port = (byte)(block >> 8); SectorAddress3Port = (byte)(block >> 16); CommandPort = CommandEnum.WriteSectors; bool xPoll = true; bool xError = false; do { var xStatus = StatusPort; if ((!xStatus.HasFlags(RegularStatusFlagsEnum.Busy)) && xStatus.HasFlags(RegularStatusFlagsEnum.DataRequest)) { xPoll = false; } if (xStatus.HasFlags(RegularStatusFlagsEnum.Error) || xStatus.HasFlags(RegularStatusFlagsEnum.DriveFault)) { xPoll = false; xError = true; } } while (xPoll); if (xError) { throw new Exception("Error while writing sector!"); } RealWriteBlock_PIO(source); } internal void WriteSector_LBA28(bool masterDrive, uint block, AddressSpace source) { var xDriveSelect = (byte)0xE0; if (!masterDrive) { xDriveSelect = 0xF0; } DriveSelectPort = (byte)(xDriveSelect | ((block >> 24) & 0xF)); SectorCountPort = 1; SectorAddress1Port = (byte)block; SectorAddress2Port = (byte)(block >> 8); SectorAddress3Port = (byte)(block >> 16); CommandPort = CommandEnum.WriteSectors; bool xPoll = true; bool xError = false; do { var xStatus = StatusPort; if ((!xStatus.HasFlags(RegularStatusFlagsEnum.Busy)) && xStatus.HasFlags(RegularStatusFlagsEnum.DataRequest)) { xPoll = false; } if (xStatus.HasFlags(RegularStatusFlagsEnum.Error) || xStatus.HasFlags(RegularStatusFlagsEnum.DriveFault)) { xPoll = false; xError = true; } } while (xPoll); if (xError) { throw new Exception("Error while writing sector!"); } RealWriteBlock_PIO(source); } #endregion } }