Add MMC/SD command to send SET_BLOCK_COUNT, READ_MULTIPLE_BLOCK and STOP_TRANSMISSION in a single call.

This commit is contained in:
2020-12-12 21:03:44 +00:00
parent 5e39e581ef
commit a026b525a2
3 changed files with 205 additions and 35 deletions

View File

@@ -42,7 +42,9 @@ using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Metadata; using Aaru.CommonTypes.Metadata;
using Aaru.Core.Logging; using Aaru.Core.Logging;
using Aaru.Decoders.MMC; using Aaru.Decoders.MMC;
using Aaru.Decoders.SecureDigital;
using Schemas; using Schemas;
using CSD = Aaru.Decoders.MMC.CSD;
using DeviceType = Aaru.CommonTypes.Enums.DeviceType; using DeviceType = Aaru.CommonTypes.Enums.DeviceType;
using MediaType = Aaru.CommonTypes.MediaType; using MediaType = Aaru.CommonTypes.MediaType;
using Version = Aaru.CommonTypes.Interop.Version; using Version = Aaru.CommonTypes.Interop.Version;
@@ -85,6 +87,7 @@ namespace Aaru.Core.Devices.Dumping
uint physicalBlockSize = 0; uint physicalBlockSize = 0;
bool byteAddressed = true; bool byteAddressed = true;
uint[] response; uint[] response;
bool supportsCmd23 = false;
Dictionary<MediaTagType, byte[]> mediaTags = new Dictionary<MediaTagType, byte[]>(); Dictionary<MediaTagType, byte[]> mediaTags = new Dictionary<MediaTagType, byte[]>();
@@ -104,6 +107,9 @@ namespace Aaru.Core.Devices.Dumping
mediaTags.Add(MediaTagType.MMC_CSD, null); mediaTags.Add(MediaTagType.MMC_CSD, null);
// Found at least since MMC System Specification 3.31
supportsCmd23 = csdDecoded.Version >= 3;
if(csdDecoded.Size == 0xFFF) if(csdDecoded.Size == 0xFFF)
{ {
UpdateStatus?.Invoke("Reading Extended CSD"); UpdateStatus?.Invoke("Reading Extended CSD");
@@ -215,7 +221,12 @@ namespace Aaru.Core.Devices.Dumping
scr = null; scr = null;
} }
else else
{
supportsCmd23 = Decoders.SecureDigital.Decoders.DecodeSCR(scr)?.CommandSupport.
HasFlag(CommandSupport.SetBlockCount) ?? false;
mediaTags.Add(MediaTagType.SD_SCR, null); mediaTags.Add(MediaTagType.SD_SCR, null);
}
break; break;
} }
@@ -254,9 +265,39 @@ namespace Aaru.Core.Devices.Dumping
byte[] cmdBuf; byte[] cmdBuf;
bool error; bool error;
if(blocksToRead > _maximumReadable)
blocksToRead = (ushort)_maximumReadable;
if(supportsCmd23 && blocksToRead > 1)
{
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) while(true)
{ {
error = _dev.Read(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed, timeout, out duration); error = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed,
timeout, out duration);
if(error) if(error)
blocksToRead /= 2; blocksToRead /= 2;
@@ -269,13 +310,24 @@ namespace Aaru.Core.Devices.Dumping
if(error) if(error)
{ {
_dumpLog.WriteLine("ERROR: Cannot get blocks to read, device error {0}.", _dev.LastError); _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.");
StoppingErrorMessage?.
Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length.");
return; return;
} }
}
if(supportsCmd23 || blocksToRead == 1)
{
UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time.");
_dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); _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);
}
if(_skip < blocksToRead) if(_skip < blocksToRead)
_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, UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i,
(long)blocks); (long)blocks);
error = _dev.Read(out cmdBuf, out response, (uint)i, blockSize, blocksToRead, byteAddressed, timeout, if(blocksToRead == 1)
error = _dev.ReadSingleBlock(out cmdBuf, out _, (uint)i, blockSize, byteAddressed, timeout,
out duration); 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) if(!error)
{ {
@@ -588,8 +647,8 @@ namespace Aaru.Core.Devices.Dumping
PulseProgress?.Invoke($"Trimming sector {badSector}"); PulseProgress?.Invoke($"Trimming sector {badSector}");
error = _dev.Read(out cmdBuf, out response, (uint)badSector, blockSize, 1, byteAddressed, timeout, error = _dev.ReadSingleBlock(out cmdBuf, out response, (uint)badSector, blockSize, byteAddressed,
out duration); timeout, out duration);
totalDuration += duration; totalDuration += duration;
@@ -640,8 +699,8 @@ namespace Aaru.Core.Devices.Dumping
forward ? "forward" : "reverse", forward ? "forward" : "reverse",
runningPersistent ? "recovering partial data, " : "")); runningPersistent ? "recovering partial data, " : ""));
error = _dev.Read(out cmdBuf, out response, (uint)badSector, blockSize, 1, byteAddressed, timeout, error = _dev.ReadSingleBlock(out cmdBuf, out response, (uint)badSector, blockSize, byteAddressed,
out duration); timeout, out duration);
totalDuration += duration; totalDuration += duration;

View File

@@ -34,6 +34,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Aaru.Core.Logging; using Aaru.Core.Logging;
using Aaru.Decoders.MMC; using Aaru.Decoders.MMC;
using Aaru.Decoders.SecureDigital;
using CSD = Aaru.Decoders.MMC.CSD;
using DeviceType = Aaru.CommonTypes.Enums.DeviceType; using DeviceType = Aaru.CommonTypes.Enums.DeviceType;
// ReSharper disable JoinDeclarationAndInitializer // ReSharper disable JoinDeclarationAndInitializer
@@ -55,6 +57,7 @@ namespace Aaru.Core.Devices.Scanning
ushort blocksToRead = 128; ushort blocksToRead = 128;
uint blockSize = 512; uint blockSize = 512;
bool byteAddressed = true; bool byteAddressed = true;
bool supportsCmd23 = false;
switch(_dev.Type) switch(_dev.Type)
{ {
@@ -68,6 +71,9 @@ namespace Aaru.Core.Devices.Scanning
results.Blocks = (ulong)((csd.Size + 1) * Math.Pow(2, csd.SizeMultiplier + 2)); results.Blocks = (ulong)((csd.Size + 1) * Math.Pow(2, csd.SizeMultiplier + 2));
blockSize = (uint)Math.Pow(2, csd.ReadBlockLength); blockSize = (uint)Math.Pow(2, csd.ReadBlockLength);
// Found at least since MMC System Specification 3.31
supportsCmd23 = csd.Version >= 3;
if(csd.Size == 0xFFF) if(csd.Size == 0xFFF)
{ {
sense = _dev.ReadExtendedCsd(out cmdBuf, out _, timeout, out _); sense = _dev.ReadExtendedCsd(out cmdBuf, out _, timeout, out _);
@@ -115,6 +121,12 @@ namespace Aaru.Core.Devices.Scanning
results.Blocks *= ratio; results.Blocks *= ratio;
blockSize = 512; blockSize = 512;
} }
sense = _dev.ReadScr(out cmdBuf, out _, timeout, out _);
if(!sense)
supportsCmd23 = Decoders.SecureDigital.Decoders.DecodeSCR(cmdBuf)?.CommandSupport.
HasFlag(CommandSupport.SetBlockCount) ?? false;
} }
break; break;
@@ -128,9 +140,36 @@ namespace Aaru.Core.Devices.Scanning
return results; return results;
} }
if(supportsCmd23)
{
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 results;
}
}
if(supportsCmd23)
{
while(true) while(true)
{ {
sense = _dev.Read(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed, timeout, out duration); sense = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed,
timeout, out duration);
if(sense) if(sense)
blocksToRead /= 2; blocksToRead /= 2;
@@ -142,10 +181,12 @@ namespace Aaru.Core.Devices.Scanning
if(sense) if(sense)
{ {
StoppingErrorMessage?.Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); StoppingErrorMessage?.
Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length.");
return results; return results;
} }
}
results.A = 0; // <3ms results.A = 0; // <3ms
results.B = 0; // >=3ms, <10ms results.B = 0; // >=3ms, <10ms
@@ -164,11 +205,14 @@ namespace Aaru.Core.Devices.Scanning
results.SeekMax = double.MinValue; results.SeekMax = double.MinValue;
results.SeekMin = double.MaxValue; results.SeekMin = double.MaxValue;
results.SeekTotal = 0; results.SeekTotal = 0;
const int SEEK_TIMES = 100; const int seekTimes = 100;
var rnd = new Random(); var rnd = new Random();
if(supportsCmd23 || blocksToRead == 1)
UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); 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); InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, sdProfile);
var mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false); 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, UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)", (long)i,
(long)results.Blocks); (long)results.Blocks);
bool error = _dev.Read(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, byteAddressed, timeout, bool error;
if(blocksToRead == 1)
error = _dev.ReadSingleBlock(out cmdBuf, out _, (uint)i, blockSize, byteAddressed, timeout,
out duration); 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) if(!error)
{ {
@@ -256,7 +309,7 @@ namespace Aaru.Core.Devices.Scanning
InitProgress?.Invoke(); InitProgress?.Invoke();
for(int i = 0; i < SEEK_TIMES; i++) for(int i = 0; i < seekTimes; i++)
{ {
if(_aborted || !_seekTest) if(_aborted || !_seekTest)
break; break;
@@ -285,7 +338,7 @@ namespace Aaru.Core.Devices.Scanning
results.ProcessingTime /= 1000; results.ProcessingTime /= 1000;
results.TotalTime = (end - start).TotalSeconds; results.TotalTime = (end - start).TotalSeconds;
results.AvgSpeed = blockSize * (double)(results.Blocks + 1) / 1048576 / results.ProcessingTime; results.AvgSpeed = blockSize * (double)(results.Blocks + 1) / 1048576 / results.ProcessingTime;
results.SeekTimes = SEEK_TIMES; results.SeekTimes = seekTimes;
return results; return results;
} }

View File

@@ -243,5 +243,63 @@ namespace Aaru.Devices
return sense; 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;
}
} }
} }