diff --git a/Aaru.Core/Devices/Dumping/SecureDigital.cs b/Aaru.Core/Devices/Dumping/SecureDigital.cs index 93338d43e..b10eb3eaa 100644 --- a/Aaru.Core/Devices/Dumping/SecureDigital.cs +++ b/Aaru.Core/Devices/Dumping/SecureDigital.cs @@ -42,7 +42,9 @@ using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Metadata; using Aaru.Core.Logging; using Aaru.Decoders.MMC; +using Aaru.Decoders.SecureDigital; using Schemas; +using CSD = Aaru.Decoders.MMC.CSD; using DeviceType = Aaru.CommonTypes.Enums.DeviceType; using MediaType = Aaru.CommonTypes.MediaType; using Version = Aaru.CommonTypes.Interop.Version; @@ -85,6 +87,7 @@ namespace Aaru.Core.Devices.Dumping uint physicalBlockSize = 0; bool byteAddressed = true; uint[] response; + bool supportsCmd23 = false; Dictionary mediaTags = new Dictionary(); @@ -104,6 +107,9 @@ namespace Aaru.Core.Devices.Dumping mediaTags.Add(MediaTagType.MMC_CSD, null); + // Found at least since MMC System Specification 3.31 + supportsCmd23 = csdDecoded.Version >= 3; + if(csdDecoded.Size == 0xFFF) { UpdateStatus?.Invoke("Reading Extended CSD"); @@ -215,7 +221,12 @@ namespace Aaru.Core.Devices.Dumping scr = null; } else + { + supportsCmd23 = Decoders.SecureDigital.Decoders.DecodeSCR(scr)?.CommandSupport. + HasFlag(CommandSupport.SetBlockCount) ?? false; + mediaTags.Add(MediaTagType.SD_SCR, null); + } break; } @@ -254,28 +265,69 @@ namespace Aaru.Core.Devices.Dumping byte[] cmdBuf; bool error; - while(true) + if(blocksToRead > _maximumReadable) + blocksToRead = (ushort)_maximumReadable; + + if(supportsCmd23 && blocksToRead > 1) { - error = _dev.Read(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed, timeout, out duration); + sense = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, 1, byteAddressed, timeout, + out duration); + + if(sense || _dev.Error) + { + UpdateStatus?. + Invoke("Environment does not support setting block count, downgrading to OS reading."); + + supportsCmd23 = false; + } + + // Need to restart device, otherwise is it just busy streaming data with no one listening + sense = _dev.ReOpen(); + + if(sense) + { + StoppingErrorMessage?.Invoke($"Error {_dev.LastError} reopening device."); + + return; + } + } + + if(supportsCmd23 && blocksToRead > 1) + { + while(true) + { + error = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed, + timeout, out duration); + + if(error) + blocksToRead /= 2; + + if(!error || + blocksToRead == 1) + break; + } if(error) - blocksToRead /= 2; + { + _dumpLog.WriteLine("ERROR: Cannot get blocks to read, device error {0}.", _dev.LastError); - if(!error || - blocksToRead == 1) - break; + StoppingErrorMessage?. + Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); + + return; + } } - if(error) + if(supportsCmd23 || blocksToRead == 1) { - _dumpLog.WriteLine("ERROR: Cannot get blocks to read, device error {0}.", _dev.LastError); - StoppingErrorMessage?.Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); - - return; + UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); + _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); + } + else + { + UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks using sequential commands."); + _dumpLog.WriteLine("Device can read {0} blocks using sequential commands.", blocksToRead); } - - UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); - _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); if(_skip < blocksToRead) _skip = blocksToRead; @@ -491,8 +543,15 @@ namespace Aaru.Core.Devices.Dumping UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, (long)blocks); - error = _dev.Read(out cmdBuf, out response, (uint)i, blockSize, blocksToRead, byteAddressed, timeout, - out duration); + if(blocksToRead == 1) + error = _dev.ReadSingleBlock(out cmdBuf, out _, (uint)i, blockSize, byteAddressed, timeout, + out duration); + else if(supportsCmd23) + error = _dev.ReadWithBlockCount(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, byteAddressed, + timeout, out duration); + else + error = _dev.ReadMultipleUsingSingle(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, + byteAddressed, timeout, out duration); if(!error) { @@ -588,8 +647,8 @@ namespace Aaru.Core.Devices.Dumping PulseProgress?.Invoke($"Trimming sector {badSector}"); - error = _dev.Read(out cmdBuf, out response, (uint)badSector, blockSize, 1, byteAddressed, timeout, - out duration); + error = _dev.ReadSingleBlock(out cmdBuf, out response, (uint)badSector, blockSize, byteAddressed, + timeout, out duration); totalDuration += duration; @@ -640,8 +699,8 @@ namespace Aaru.Core.Devices.Dumping forward ? "forward" : "reverse", runningPersistent ? "recovering partial data, " : "")); - error = _dev.Read(out cmdBuf, out response, (uint)badSector, blockSize, 1, byteAddressed, timeout, - out duration); + error = _dev.ReadSingleBlock(out cmdBuf, out response, (uint)badSector, blockSize, byteAddressed, + timeout, out duration); totalDuration += duration; diff --git a/Aaru.Core/Devices/Scanning/SecureDigital.cs b/Aaru.Core/Devices/Scanning/SecureDigital.cs index 540ca4020..006b75edb 100644 --- a/Aaru.Core/Devices/Scanning/SecureDigital.cs +++ b/Aaru.Core/Devices/Scanning/SecureDigital.cs @@ -34,6 +34,8 @@ using System; using System.Collections.Generic; using Aaru.Core.Logging; using Aaru.Decoders.MMC; +using Aaru.Decoders.SecureDigital; +using CSD = Aaru.Decoders.MMC.CSD; using DeviceType = Aaru.CommonTypes.Enums.DeviceType; // ReSharper disable JoinDeclarationAndInitializer @@ -55,6 +57,7 @@ namespace Aaru.Core.Devices.Scanning ushort blocksToRead = 128; uint blockSize = 512; bool byteAddressed = true; + bool supportsCmd23 = false; switch(_dev.Type) { @@ -68,6 +71,9 @@ namespace Aaru.Core.Devices.Scanning results.Blocks = (ulong)((csd.Size + 1) * Math.Pow(2, csd.SizeMultiplier + 2)); blockSize = (uint)Math.Pow(2, csd.ReadBlockLength); + // Found at least since MMC System Specification 3.31 + supportsCmd23 = csd.Version >= 3; + if(csd.Size == 0xFFF) { sense = _dev.ReadExtendedCsd(out cmdBuf, out _, timeout, out _); @@ -115,6 +121,12 @@ namespace Aaru.Core.Devices.Scanning results.Blocks *= ratio; blockSize = 512; } + + sense = _dev.ReadScr(out cmdBuf, out _, timeout, out _); + + if(!sense) + supportsCmd23 = Decoders.SecureDigital.Decoders.DecodeSCR(cmdBuf)?.CommandSupport. + HasFlag(CommandSupport.SetBlockCount) ?? false; } break; @@ -128,23 +140,52 @@ namespace Aaru.Core.Devices.Scanning return results; } - while(true) + if(supportsCmd23) { - sense = _dev.Read(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed, timeout, out duration); + sense = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, 1, byteAddressed, timeout, + out duration); + + if(sense || _dev.Error) + { + UpdateStatus?. + Invoke("Environment does not support setting block count, downgrading to OS reading."); + + supportsCmd23 = false; + } + + // Need to restart device, otherwise is it just busy streaming data with no one listening + sense = _dev.ReOpen(); if(sense) - blocksToRead /= 2; + { + StoppingErrorMessage?.Invoke($"Error {_dev.LastError} reopening device."); - if(!sense || - blocksToRead == 1) - break; + return results; + } } - if(sense) + if(supportsCmd23) { - StoppingErrorMessage?.Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); + while(true) + { + sense = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed, + timeout, out duration); - return results; + if(sense) + blocksToRead /= 2; + + if(!sense || + blocksToRead == 1) + break; + } + + if(sense) + { + StoppingErrorMessage?. + Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); + + return results; + } } results.A = 0; // <3ms @@ -164,11 +205,14 @@ namespace Aaru.Core.Devices.Scanning results.SeekMax = double.MinValue; results.SeekMin = double.MaxValue; results.SeekTotal = 0; - const int SEEK_TIMES = 100; + const int seekTimes = 100; var rnd = new Random(); - UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); + if(supportsCmd23 || blocksToRead == 1) + UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); + else + UpdateStatus?.Invoke($"Reading {blocksToRead} sectors using sequential single commands."); InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, sdProfile); var mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false); @@ -198,8 +242,17 @@ namespace Aaru.Core.Devices.Scanning UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, (long)results.Blocks); - bool error = _dev.Read(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, byteAddressed, timeout, - out duration); + bool error; + + if(blocksToRead == 1) + error = _dev.ReadSingleBlock(out cmdBuf, out _, (uint)i, blockSize, byteAddressed, timeout, + out duration); + else if(supportsCmd23) + error = _dev.ReadWithBlockCount(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, byteAddressed, + timeout, out duration); + else + error = _dev.ReadMultipleUsingSingle(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, + byteAddressed, timeout, out duration); if(!error) { @@ -256,7 +309,7 @@ namespace Aaru.Core.Devices.Scanning InitProgress?.Invoke(); - for(int i = 0; i < SEEK_TIMES; i++) + for(int i = 0; i < seekTimes; i++) { if(_aborted || !_seekTest) break; @@ -285,7 +338,7 @@ namespace Aaru.Core.Devices.Scanning results.ProcessingTime /= 1000; results.TotalTime = (end - start).TotalSeconds; results.AvgSpeed = blockSize * (double)(results.Blocks + 1) / 1048576 / results.ProcessingTime; - results.SeekTimes = SEEK_TIMES; + results.SeekTimes = seekTimes; return results; } diff --git a/Aaru.Devices/Device/MmcCommands/MMC.cs b/Aaru.Devices/Device/MmcCommands/MMC.cs index 25a3545d6..3b335f1c2 100644 --- a/Aaru.Devices/Device/MmcCommands/MMC.cs +++ b/Aaru.Devices/Device/MmcCommands/MMC.cs @@ -243,5 +243,63 @@ namespace Aaru.Devices return sense; } + + public bool ReadWithBlockCount(out byte[] buffer, out uint[] response, uint lba, uint blockSize, + ushort transferLength, bool byteAddressed, uint timeout, out double duration) + { + uint address = byteAddressed ? lba * blockSize : lba; + MmcSingleCommand[] commands = new MmcSingleCommand[3]; + + // SET_BLOCK_COUNT + commands[0] = new MmcSingleCommand + { + command = MmcCommands.SetBlockCount, + write = false, + isApplication = false, + flags = MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAc, + argument = transferLength, + blockSize = 0, + blocks = 0, + buffer = new byte[0] + }; + + // READ_MULTIPLE_BLOCK + commands[1] = new MmcSingleCommand + { + command = MmcCommands.ReadMultipleBlock, + write = false, + isApplication = false, + flags = MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, + argument = address, + blockSize = blockSize, + blocks = transferLength, + buffer = new byte[transferLength * blockSize] + }; + + // STOP_TRANSMISSION + // Needed if the previous command fails + commands[2] = new MmcSingleCommand + { + command = MmcCommands.StopTransmission, + write = false, + isApplication = false, + flags = MmcFlags.ResponseSpiR1B | MmcFlags.ResponseR1B | MmcFlags.CommandAc, + argument = 0, + blockSize = 0, + blocks = 0, + buffer = new byte[0] + }; + + LastError = SendMultipleMmcCommands(commands, out duration, out bool sense, timeout); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SecureDigital Device", "READ_MULTIPLE_BLOCK took {0} ms.", duration); + + buffer = commands[1].buffer; + response = commands[1].response; + + return sense; + } } } \ No newline at end of file