1 // 2 // Copyright (c) 2010-2020 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using System.Collections.Generic; 9 using System.IO; 10 using System.Linq; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Core.Structure; 13 using Antmicro.Renode.Exceptions; 14 using Antmicro.Renode.Logging; 15 using Antmicro.Renode.Storage; 16 using Antmicro.Renode.Storage.SCSI; 17 using Antmicro.Renode.Storage.SCSI.Commands; 18 using Antmicro.Renode.Utilities; 19 using Antmicro.Renode.Utilities.Packets; 20 21 namespace Antmicro.Renode.Peripherals.ATAPI 22 { 23 //This device implements ATAPI CDROM 24 public class CDROM : IAtapiPeripheral, IDisposable 25 { CDROM(string imageFile, bool persistent = false, uint? size = null, uint blockSize = 2048)26 public CDROM(string imageFile, bool persistent = false, uint? size = null, uint blockSize = 2048) 27 { 28 BlockSize = blockSize; 29 dataBackend = DataStorage.Create(imageFile, size, persistent); 30 31 var sizeMisalignment = dataBackend.Length % blockSize; 32 if(sizeMisalignment != 0) 33 { 34 dataBackend.SetLength(dataBackend.Length + (blockSize - sizeMisalignment)); 35 this.Log(LogLevel.Warning, "Underlying data size extended by {0} bytes to align it to the block size ({1} bytes)", blockSize - sizeMisalignment, blockSize); 36 } 37 } 38 Dispose()39 public void Dispose() 40 { 41 dataBackend.Dispose(); 42 } 43 Reset()44 public void Reset() 45 { 46 dataQueue.Clear(); 47 dataBackend.Position = 0; 48 } 49 SendIdentifyResponse()50 public void SendIdentifyResponse() 51 { 52 //First byte of Identify device responser - sets device type 53 var response = new byte[] {0x00, 0x05}; 54 QueueData(response); 55 } 56 DequeueData()57 public ushort DequeueData() 58 { 59 return dataQueue.TryDequeue(out var ret) ? ret : (ushort)0; 60 } 61 HandleCommand(byte[] packet)62 public void HandleCommand(byte[] packet) 63 { 64 var command = SCSICommandDescriptorBlock.DecodeCommand(packet, 0); 65 this.Log(LogLevel.Debug, "Decoded command: {0}", command); 66 switch(command) 67 { 68 case SCSICommand.TestUnitReady: 69 break; 70 case SCSICommand.Inquiry: 71 // this is just an empty stub 72 QueueData(new byte[36]); 73 break; 74 case SCSICommand.Read10: 75 var cmd = Packet.DecodeDynamic<IReadWrite10Command>(packet, 0); 76 this.Log(LogLevel.Debug, "Command args: LogicalBlockAddress: 0x{0:x}, TransferLength: {1}", (uint)cmd.LogicalBlockAddress, (ushort)cmd.TransferLength); 77 var bytesCount = (int)(cmd.TransferLength * BlockSize); 78 var readPosition = (long)cmd.LogicalBlockAddress * BlockSize; 79 dataBackend.Position = readPosition; 80 var data = dataBackend.ReadBytes(bytesCount); 81 this.Log(LogLevel.Debug, "Reading {0} bytes from address 0x{1:x}", bytesCount, readPosition); 82 QueueData(data); 83 break; 84 case SCSICommand.ModeSense6: 85 // this is just an empty stub 86 QueueData(new byte[192]); 87 break; 88 case SCSICommand.RequestSense: 89 // this is just an empty stub 90 QueueData(new byte[512]); 91 break; 92 default: 93 this.Log(LogLevel.Error, "Unsupported SCSI command: {0}", command); 94 break; 95 } 96 } 97 98 public uint BlockSize { get; } 99 public bool DataReady { get { return dataQueue.Count != 0;}} 100 QueueData(byte[] data)101 private void QueueData(byte[] data) 102 { 103 var dataLength = data.Length; 104 if(dataLength % 2 != 0) 105 { 106 this.Log(LogLevel.Error, "Trying to send odd number of bytes. Padding with zeros"); 107 data = data.Concat(new byte[] { 0 }).ToArray(); 108 dataLength += 1; 109 } 110 111 for(uint i = 0; i < dataLength - 1; i += 2) 112 { 113 var val = data[i] | (data[i + 1] << 8); 114 dataQueue.Enqueue((ushort)val); 115 } 116 } 117 118 private readonly Queue<ushort> dataQueue = new Queue<ushort>(); 119 120 private readonly Stream dataBackend; 121 } 122 } 123 124