Add MMC/SecureDigital device support. Not yet used because of

a bad implementation of SEND_CSD and SEND_CID commands (TODO).
This commit is contained in:
2016-10-22 22:58:01 +01:00
parent 8b9d678893
commit 0c9dfaa11f
24 changed files with 3607 additions and 3 deletions

View File

@@ -1,3 +1,19 @@
2016-10-22 Natalia Portillo <claunia@claunia.com>
* Enums.cs:
* Command.cs:
* Enums.cs:
* Extern.cs:
* Command.cs:
* Structs.cs:
* Commands.cs:
* Constructor.cs:
* MMC.cs:
* DiscImageChef.Devices.csproj:
* SecureDigital.cs: Add MMC/SecureDigital device support. Not
yet used because of a bad implementation of SEND_CSD and
SEND_CID commands (TODO).
2016-10-17 Natalia Portillo <claunia@claunia.com>
* Variables.cs:

View File

@@ -219,6 +219,34 @@ namespace DiscImageChef.Devices
throw new InvalidOperationException(string.Format("Platform {0} not yet supported.", ptID));
}
}
public static int SendMmcCommand(object fd, MmcCommands command, bool write, bool isApplication,
MmcFlags flags, uint argument, uint blockSize, uint blocks,
ref byte[] buffer, out uint[] response, out double duration, out bool sense, uint timeout = 0)
{
Interop.PlatformID ptID = DetectOS.GetRealPlatformID();
return SendMmcCommand(ptID, (int)fd, command, write, isApplication, flags, argument, blockSize, blocks, ref buffer, out response, out duration, out sense, timeout);
}
public static int SendMmcCommand(Interop.PlatformID ptID, object fd, MmcCommands command, bool write, bool isApplication,
MmcFlags flags, uint argument, uint blockSize, uint blocks, ref byte[] buffer,
out uint[] response, out double duration, out bool sense, uint timeout = 0)
{
switch(ptID)
{
case Interop.PlatformID.Win32NT:
{
throw new NotImplementedException();
}
case Interop.PlatformID.Linux:
{
return Linux.Command.SendMmcCommand((int)fd, command, write, isApplication, flags, argument, blockSize, blocks, ref buffer, out response, out duration, out sense, timeout);
}
default:
throw new InvalidOperationException(string.Format("Platform {0} not yet supported.", ptID));
}
}
}
}

View File

@@ -114,6 +114,30 @@ namespace DiscImageChef.Devices
return Command.SendAtaCommand(platformID, fd, registers, out errorRegisters, protocol, transferRegister,
ref buffer, timeout, transferBlocks, out duration, out sense);
}
/// <summary>
/// Sends a MMC/SD command to this device
/// </summary>
/// <returns>The result of the command.</returns>
/// <param name="command">MMC/SD opcode</param>
/// <param name="buffer">Buffer for MMC/SD command response</param>
/// <param name="timeout">Timeout in seconds</param>
/// <param name="duration">Time it took to execute the command in milliseconds</param>
/// <param name="sense"><c>True</c> if MMC/SD returned non-OK status</param>
/// <param name="write"><c>True</c> if data is sent from host to card</param>
/// <param name="isApplication"><c>True</c> if command should be preceded with CMD55</param>
/// <param name="flags">Flags indicating kind and place of response</param>
/// <param name="blocks">How many blocks to transfer</param>
/// <param name="argument">Command argument</param>
/// <param name="response">Response registers</param>
/// <param name="blockSize">Size of block in bytes</param>
public int SendMmcCommand(MmcCommands command, bool write, bool isApplication, MmcFlags flags,
uint argument, uint blockSize, uint blocks, ref byte[] buffer, out uint[] response,
out double duration, out bool sense, uint timeout = 0)
{
return Command.SendMmcCommand(platformID, fd, command, write, isApplication, flags, argument, blockSize, blocks,
ref buffer, out response, out duration, out sense, timeout);
}
}
}

View File

@@ -90,6 +90,41 @@ namespace DiscImageChef.Devices
type = DeviceType.Unknown;
scsiType = Decoders.SCSI.PeripheralDeviceTypes.UnknownDevice;
// TODO: This is getting error -110 in Linux. Apparently I should set device to standby, request CID/CSD, put device to transition. However I can't get it right now.
/*
try
{
byte[] csdBuf;
byte[] scrBuf;
uint[] mmcResponse;
double mmcDuration;
bool mmcSense = ReadCID(out csdBuf, out mmcResponse, 0, out mmcDuration);
if(!mmcSense)
{
mmcSense = ReadSCR(out scrBuf, out mmcResponse, 0, out mmcDuration);
if(!mmcSense)
type = DeviceType.SecureDigital;
else
type = DeviceType.MMC;
manufacturer = "To be filled manufacturer";
model = "To be filled model";
revision = "To be filled revision";
serial = "To be filled serial";
scsiType = Decoders.SCSI.PeripheralDeviceTypes.DirectAccess;
removable = false;
return;
}
else
System.Console.WriteLine("Error {0}: {1}", error, lastError);
}
catch(NotImplementedException) { }
catch(InvalidOperationException) { }
*/
AtaErrorRegistersCHS errorRegisters;
byte[] ataBuf;
@@ -288,7 +323,6 @@ namespace DiscImageChef.Devices
pcmcia = false;
#endregion PCMCIA
if(!scsiSense)
{
Decoders.SCSI.Inquiry.SCSIInquiry? Inquiry = Decoders.SCSI.Inquiry.Decode(inqBuf);

View File

@@ -0,0 +1,151 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : MMC.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Component
//
// --[ Description ] ----------------------------------------------------------
//
// Description
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2016 Natalia Portillo
// ****************************************************************************/
using DiscImageChef.Console;
namespace DiscImageChef.Devices
{
public partial class Device
{
public bool ReadCSD(out byte[] buffer, out uint[] response, uint timeout, out double duration)
{
buffer = new byte[16];
bool sense = false;
lastError = SendMmcCommand(MmcCommands.SendCSD, false, false, MmcFlags.ResponseSPI_R1 | MmcFlags.Response_R1 | MmcFlags.CommandADTC,
0, 16, 1, ref buffer, out response, out duration, out sense, timeout);
error = lastError != 0;
DicConsole.DebugWriteLine("MMC Device", "SEND_CSD took {0} ms.", duration);
return sense;
}
public bool ReadCID(out byte[] buffer, out uint[] response, uint timeout, out double duration)
{
buffer = new byte[16];
bool sense = false;
lastError = SendMmcCommand(MmcCommands.SendCID, false, false, MmcFlags.ResponseSPI_R1 | MmcFlags.Response_R1 | MmcFlags.CommandADTC,
0, 16, 1, ref buffer, out response, out duration, out sense, timeout);
error = lastError != 0;
DicConsole.DebugWriteLine("MMC Device", "SEND_CID took {0} ms.", duration);
return sense;
}
public bool ReadOCR(out byte[] buffer, out uint[] response, uint timeout, out double duration)
{
buffer = new byte[4];
bool sense = false;
lastError = SendMmcCommand(MmcCommands.SendOpCond, false, true, MmcFlags.ResponseSPI_R3 | MmcFlags.Response_R3 | MmcFlags.CommandBCR,
0, 4, 1, ref buffer, out response, out duration, out sense, timeout);
error = lastError != 0;
DicConsole.DebugWriteLine("SecureDigital Device", "SEND_OP_COND took {0} ms.", duration);
return sense;
}
public bool ReadExtendedCSD(out byte[] buffer, out uint[] response, uint timeout, out double duration)
{
buffer = new byte[512];
bool sense = false;
lastError = SendMmcCommand(MmcCommands.SendExtCSD, false, false, MmcFlags.ResponseSPI_R1 | MmcFlags.Response_R1 | MmcFlags.CommandADTC,
0, 512, 1, ref buffer, out response, out duration, out sense, timeout);
error = lastError != 0;
DicConsole.DebugWriteLine("MMC Device", "SEND_EXT_CSD took {0} ms.", duration);
return sense;
}
public bool SetBlockLength(uint length, out uint[] response, uint timeout, out double duration)
{
byte[] buffer = new byte[0];
bool sense = false;
lastError = SendMmcCommand(MmcCommands.SetBlocklen, false, false, MmcFlags.ResponseSPI_R1 | MmcFlags.Response_R1 | MmcFlags.CommandAC,
length, 0, 0, ref buffer, out response, out duration, out sense, timeout);
error = lastError != 0;
DicConsole.DebugWriteLine("MMC Device", "SET_BLOCKLEN took {0} ms.", duration);
return sense;
}
public bool Read(out byte[] buffer, out uint[] response, uint lba, uint blockSize, uint transferLength, bool byteAddressed, uint timeout, out double duration)
{
buffer = new byte[transferLength * blockSize];
bool sense = false;
uint address;
if(byteAddressed)
address = lba * blockSize;
else
address = lba;
MmcCommands command;
if(transferLength > 1)
command = MmcCommands.ReadMultipleBlock;
else
command = MmcCommands.ReadSingleBlock;
lastError = SendMmcCommand(command, false, false, MmcFlags.ResponseSPI_R1 | MmcFlags.Response_R1 | MmcFlags.CommandADTC,
address, blockSize, transferLength, ref buffer, out response, out duration, out sense, timeout);
error = lastError != 0;
if(transferLength > 1)
DicConsole.DebugWriteLine("MMC Device", "READ_MULTIPLE_BLOCK took {0} ms.", duration);
else
DicConsole.DebugWriteLine("MMC Device", "READ_SINGLE_BLOCK took {0} ms.", duration);
return sense;
}
public bool ReadStatus(out byte[] buffer, out uint[] response, uint timeout, out double duration)
{
buffer = new byte[4];
bool sense = false;
lastError = SendMmcCommand(MmcCommands.SendStatus, false, true, MmcFlags.ResponseSPI_R1 | MmcFlags.Response_R1 | MmcFlags.CommandADTC,
0, 4, 1, ref buffer, out response, out duration, out sense, timeout);
error = lastError != 0;
DicConsole.DebugWriteLine("SecureDigital Device", "SEND_STATUS took {0} ms.", duration);
return sense;
}
}
}

View File

@@ -0,0 +1,81 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : SecureDigital.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Component
//
// --[ Description ] ----------------------------------------------------------
//
// Description
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2016 Natalia Portillo
// ****************************************************************************/
using DiscImageChef.Console;
namespace DiscImageChef.Devices
{
public partial class Device
{
public bool ReadSDStatus(out byte[] buffer, out uint[] response, uint timeout, out double duration)
{
buffer = new byte[64];
bool sense = false;
lastError = SendMmcCommand((MmcCommands)SecureDigitalCommands.SendStatus, false, true, MmcFlags.ResponseSPI_R1 | MmcFlags.Response_R1 | MmcFlags.CommandADTC,
0, 64, 1, ref buffer, out response, out duration, out sense, timeout);
error = lastError != 0;
DicConsole.DebugWriteLine("SecureDigital Device", "SD_STATUS took {0} ms.", duration);
return sense;
}
public bool ReadSDOCR(out byte[] buffer, out uint[] response, uint timeout, out double duration)
{
buffer = new byte[4];
bool sense = false;
lastError = SendMmcCommand((MmcCommands)SecureDigitalCommands.SendOperatingCondition, false, true, MmcFlags.ResponseSPI_R3 | MmcFlags.Response_R3 | MmcFlags.CommandBCR,
0, 4, 1, ref buffer, out response, out duration, out sense, timeout);
error = lastError != 0;
DicConsole.DebugWriteLine("SecureDigital Device", "SD_SEND_OP_COND took {0} ms.", duration);
return sense;
}
public bool ReadSCR(out byte[] buffer, out uint[] response, uint timeout, out double duration)
{
buffer = new byte[8];
bool sense = false;
lastError = SendMmcCommand((MmcCommands)SecureDigitalCommands.SendSCR, false, true, MmcFlags.ResponseSPI_R1 | MmcFlags.Response_R1 | MmcFlags.CommandADTC,
0, 8, 1, ref buffer, out response, out duration, out sense, timeout);
error = lastError != 0;
DicConsole.DebugWriteLine("SecureDigital Device", "SEND_SCR took {0} ms.", duration);
return sense;
}
}
}

View File

@@ -76,6 +76,8 @@
<Compile Include="FreeBSD\Enums.cs" />
<Compile Include="FreeBSD\Extern.cs" />
<Compile Include="FreeBSD\Structs.cs" />
<Compile Include="Device\MmcCommands\MMC.cs" />
<Compile Include="Device\MmcCommands\SecureDigital.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
@@ -85,6 +87,7 @@
<Folder Include="Device\ScsiCommands\" />
<Folder Include="Device\AtaCommands\" />
<Folder Include="FreeBSD\" />
<Folder Include="Device\MmcCommands\" />
</ItemGroup>
<ProjectExtensions>
<MonoDevelop>
@@ -92,8 +95,8 @@
<Policies>
<DotNetNamingPolicy DirectoryNamespaceAssociation="PrefixedHierarchical" ResourceNamePolicy="MSBuild" />
<StandardHeader Text="/***************************************************************************&#xA;The Disc Image Chef&#xA;----------------------------------------------------------------------------&#xA; &#xA;Filename : ${FileName}&#xA;Author(s) : ${AuthorName} &lt;${AuthorEmail}&gt;&#xA;&#xA;Component : Component&#xA; &#xA;--[ Description ] ----------------------------------------------------------&#xA; &#xA; Description&#xA; &#xA;--[ License ] --------------------------------------------------------------&#xA; &#xA; This library is free software; you can redistribute it and/or modify&#xA; it under the terms of the GNU Lesser General Public License as&#xA; published by the Free Software Foundation; either version 2.1 of the&#xA; License, or (at your option) any later version.&#xA;&#xA; This library is distributed in the hope that it will be useful, but&#xA; WITHOUT ANY WARRANTY; without even the implied warranty of&#xA; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU&#xA; Lesser General Public License for more details.&#xA;&#xA; You should have received a copy of the GNU Lesser General Public&#xA; License along with this library; if not, see &lt;http://www.gnu.org/licenses/&gt;.&#xA;&#xA;----------------------------------------------------------------------------&#xA;Copyright © 2011-${Year} ${CopyrightHolder}&#xA;****************************************************************************/" IncludeInNewFiles="True" />
<TextStylePolicy FileWidth="120" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/x-csharp" />
<CSharpFormattingPolicy IndentSwitchSection="True" NewLinesForBracesInProperties="True" NewLinesForBracesInAccessors="True" NewLinesForBracesInAnonymousMethods="True" NewLinesForBracesInControlBlocks="True" NewLinesForBracesInAnonymousTypes="True" NewLinesForBracesInObjectCollectionArrayInitializers="True" NewLinesForBracesInLambdaExpressionBody="True" NewLineForElse="True" NewLineForCatch="True" NewLineForFinally="True" NewLineForMembersInObjectInit="True" NewLineForMembersInAnonymousTypes="True" NewLineForClausesInQuery="True" SpacingAfterMethodDeclarationName="False" SpaceAfterMethodCallName="False" SpaceAfterControlFlowStatementKeyword="False" SpaceBeforeOpenSquareBracket="False" inheritsSet="Mono" inheritsScope="text/x-csharp" scope="text/x-csharp" />
<TextStylePolicy TabWidth="4" TabsToSpaces="True" IndentWidth="4" RemoveTrailingWhitespace="True" NoTabsAfterNonTabs="False" EolMarker="Native" FileWidth="120" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/x-csharp" />
<CSharpFormattingPolicy IndentBlock="True" IndentBraces="False" IndentSwitchCaseSection="True" LabelPositioning="OneLess" NewLinesForBracesInTypes="True" NewLinesForBracesInMethods="True" SpaceWithinMethodDeclarationParenthesis="False" SpaceBetweenEmptyMethodDeclarationParentheses="False" SpaceWithinMethodCallParentheses="False" SpaceBetweenEmptyMethodCallParentheses="False" SpaceWithinExpressionParentheses="False" SpaceWithinCastParentheses="False" SpaceWithinOtherParentheses="False" SpaceAfterCast="False" SpacesIgnoreAroundVariableDeclaration="False" SpaceBetweenEmptySquareBrackets="False" SpaceWithinSquareBrackets="False" SpaceAfterColonInBaseTypeDeclaration="True" SpaceAfterComma="True" SpaceAfterDot="False" SpaceAfterSemicolonsInForStatement="True" SpaceBeforeColonInBaseTypeDeclaration="True" SpaceBeforeComma="False" SpaceBeforeDot="False" SpaceBeforeSemicolonsInForStatement="False" SpacingAroundBinaryOperator="Single" WrappingPreserveSingleLine="True" WrappingKeepStatementsOnSingleLine="True" PlaceSystemDirectiveFirst="True" IndentSwitchSection="True" NewLinesForBracesInProperties="True" NewLinesForBracesInAccessors="True" NewLinesForBracesInAnonymousMethods="True" NewLinesForBracesInControlBlocks="True" NewLinesForBracesInAnonymousTypes="True" NewLinesForBracesInObjectCollectionArrayInitializers="True" NewLinesForBracesInLambdaExpressionBody="True" NewLineForElse="True" NewLineForCatch="True" NewLineForFinally="True" NewLineForMembersInObjectInit="True" NewLineForMembersInAnonymousTypes="True" NewLineForClausesInQuery="True" SpacingAfterMethodDeclarationName="False" SpaceAfterMethodCallName="False" SpaceAfterControlFlowStatementKeyword="False" SpaceBeforeOpenSquareBracket="False" inheritsSet="Mono" inheritsScope="text/x-csharp" scope="text/x-csharp" />
</Policies>
</Properties>
</MonoDevelop>

View File

@@ -3455,5 +3455,355 @@ namespace DiscImageChef.Devices
Obsolete1 = 4,
Obsolete2 = 5
}
/// <summary>
/// MMC / SecureDigital commands
/// </summary>
public enum MmcCommands : byte
{
#region Class 1 MMC Commands (Basic and read-stream)
/// <summary>
/// Resets device to idle (BC)
/// </summary>
GoIdle = 0,
/// <summary>
/// Resets the device to pre-idle (BC)
/// </summary>
GoPreIdleState = 0,
/// <summary>
/// Initiate alternative boot operation
/// </summary>
BootInitiation = 0,
/// <summary>
/// Asks device in idle state to send their operation conditions in response (BCR, R3)
/// </summary>
SendOpCond = 1,
/// <summary>
/// Asks device to send their CID numbers (BCR, R2)
/// </summary>
AllSendCID = 2,
/// <summary>
/// Assigns a relative address to the device (AC, R1)
/// </summary>
SetRelativeAddress = 3,
/// <summary>
/// Programs the DSR of the device (BC)
/// </summary>
SetDSR = 4,
/// <summary>
/// Toggles the device between sleep and standby (AC, R1b)
/// </summary>
SleepAwake = 5,
/// <summary>
/// Switches device mode of operation (AC, R1b)
/// </summary>
Switch = 6,
/// <summary>
/// Toggles a device between the stand-by and transfer stats or between the programming and disconnect states (AC, R1b)
/// </summary>
SelectCard = 7,
/// <summary>
/// Asks device to send its extended card-specific data (ExtCSD) (ADTC, R1)
/// </summary>
SendExtCSD = 8,
/// <summary>
/// Asks device to send its card-specific data (CSD) (AC, R2)
/// </summary>
SendCSD = 9,
/// <summary>
/// Asks device to send its card identification (CID) (AC, R2)
/// </summary>
SendCID = 10,
/// <summary>
/// Reads data stream from device, starting at given address, until a <see cref="StopTransmission"/> follows (ADTC, R1)
/// </summary>
[Obsolete]
ReadDatUntilStop = 11,
/// <summary>
/// Terminates a read/write stream/multiple block operation (AC, R1 / R1b)
/// </summary>
StopTransmission = 12,
/// <summary>
/// Asks device to send its status register (AC, R1)
/// </summary>
SendStatus = 13,
/// <summary>
/// The host reads the reversed bus testing data pattern from a device (ADTC, R1)
/// </summary>
BusTestRead = 14,
/// <summary>
/// Sets the card to inactive state (AC)
/// </summary>
GoInactiveState = 15,
/// <summary>
/// The host sends the bus testing data pattern to a device (ADTC, R1)
/// </summary>
BusTestWrite = 19,
SPIReadOCR = 58,
SPICRCOnOff = 59,
#endregion Class 1 MMC Commands (Basic and read-stream)
#region Class 2 MMC Commands (Block-oriented read)
/// <summary>
/// Sets the block length in bytes (AC, R1)
/// </summary>
SetBlocklen = 16,
/// <summary>
/// Reads a block (ADTC, R1)
/// </summary>
ReadSingleBlock = 17,
/// <summary>
/// Transfers data blocks from card to host until interrupted (ADTC, R1)
/// </summary>
ReadMultipleBlock = 18,
/// <summary>
/// 128 blocks of tuning pattern is sent for HS200 optimal sampling point detection (ADTC, R1)
/// </summary>
SendTuningBlockHS200 = 21,
#endregion Class 2 MMC Commands (Block-oriented read)
#region Class 3 MMC Commands (Stream write)
/// <summary>
/// Writes data stream from host until a <see cref="StopTransmission"/> follows (ADTC, R1)
/// </summary>
[Obsolete]
WriteDatUntilStop = 20,
#endregion Class 3 MMC Commands (Stream write)
#region Class 4 MMC Commands (Block-oriented write)
/// <summary>
/// Defines the number of blocks which are going to be transferred in the immediately succeeding multiple block command (AC, R1)
/// </summary>
SetBlockCount = 23,
/// <summary>
/// Writes a block (ADTC, R1)
/// </summary>
WriteBlock = 24,
/// <summary>
/// Continuosly writes blocks until interrupted (ADTC, R1)
/// </summary>
WriteMultipleBlock = 25,
/// <summary>
/// Programs the Card Information register (ADTC, R1)
/// </summary>
ProgramCID = 26,
/// <summary>
/// Programs the programmable bits of the CSD (ADTC, R1)
/// </summary>
ProgramCSD = 27,
/// <summary>
/// Sets the real time clock according to information in block (ADTC, R1)
/// </summary>
SetTime = 49,
#endregion Class 4 MMC Commands (Block-oriented write)
#region Class 5 MMC Commands (Erase)
/// <summary>
/// Sets the address of the first erase group (AC, R1)
/// </summary>
EraseGroupStart = 35,
/// <summary>
/// Sets the address of the last erase group (AC, R1)
/// </summary>
EraseGroupEnd = 36,
/// <summary>
/// Erases previously selected write blocks (AC, R1b)
/// </summary>
Erase = 38,
#endregion Class 5 MMC Commands (Erase)
#region Class 6 MMC Commands (Block-oriented write protection)
/// <summary>
/// Sets the write protection bit (AC, R1b)
/// </summary>
SetWriteProtect = 28,
/// <summary>
/// Clears the write protection bit (AC, R1b)
/// </summary>
ClearWriteProtect = 29,
/// <summary>
/// Asks the device to send the status of the write protection bit (ADTC, R1)
/// </summary>
SendWriteProtect = 30,
/// <summary>
/// Sends the type of write protection that is set for the different write protection groups (ADTC, R1)
/// </summary>
SentWriteProtectType = 31,
#endregion Class 6 MMC Commands (Block-oriented write protection)
#region Class 7 MMC Commands (Lock)
/// <summary>
/// Used to set/reset the password or lock/unlock the card (ADTC, R1b)
/// </summary>
LockUnlock = 42,
#endregion Class 7 MMC Commands (Lock)
#region Class 8 MMC Commands (Application-specific)
/// <summary>
/// Indicates the card that the next command is an application specific command (AC, R1)
/// </summary>
ApplicationCommand = 55,
/// <summary>
/// Transfers a data block to/from the card for general purpose / application specific commands (ADTC, R1b)
/// </summary>
GenericCommand = 56,
#endregion Class 8 MMC Commands (Application-specific)
#region Class 9 MMC Commands (I/O mode)
/// <summary>
/// Used to write and read 8 bit data field, used to access application dependent registers not defined in MMC standard (AC, R4)
/// </summary>
FastIO = 39,
/// <summary>
/// Sets the system into interrupt mode (BCR, R5)
/// </summary>
GoIRQState = 40,
#endregion Class 9 MMC Commands (I/O mode)
#region Class 10 MMC Commands (Security Protocols)
/// <summary>
/// Reads data blocks (ADTC, R1)
/// </summary>
ProtocolRead = 53,
/// <summary>
/// Writes data blocks (ADTC, R1)
/// </summary>
ProtocolWrite = 54,
#endregion Class 10 MMC Commands (Security Protocols)
#region Class 11 MMC Commands (Command Queue)
/// <summary>
/// Defines data direction, priority, task ID and block count of queued task (AC, R1)
/// </summary>
QueuedTaskParameters = 44,
/// <summary>
/// Defines the block address of queued task (AC, R1)
/// </summary>
QueuedTaskAddress = 45,
/// <summary>
/// Executes the task queue for reading (ADTC, R1)
/// </summary>
ExecuteTaskRead = 46,
/// <summary>
/// Executes the task queue for writing (ADTC, R1)
/// </summary>
ExecuteTaskWrite = 47,
/// <summary>
/// Manages queues and tasks (AC, R1b)
/// </summary>
CmdQTaskManagement = 48,
#endregion Class 11 MMC Commands (Command Queue)
#region Class 1 SecureDigital Commands (Basic)
/// <summary>
/// Sends SD interface condition (BCR, R7)
/// </summary>
SendInterfaceCondition = 8,
/// <summary>
/// Switch to 1.8V bus signaling level (AC, R1)
/// </summary>
VoltageSwitch = 11,
#endregion Class 1 SecureDigital Commands (Basic)
#region Class 2 SecureDigital Commands (Block-oriented read)
/// <summary>
/// 64 bytes of tuning pattern is sent for SDR50 and SDR104 optinal sampling point detection (ADTC, R1)
/// </summary>
SendTuningBlock = 19,
/// <summary>
/// Speed class control command (AC, R1b)
/// </summary>
SpeedClassControl = 20,
#endregion Class 2 SecureDigital Commands (Block-oriented read)
#region Class 11 SecureDigital Commands (Function Extension)
/// <summary>
/// Single block read type (ADTC, R1)
/// </summary>
ReadExtraSingle = 48,
/// <summary>
/// Single block write type (ADTC, R1)
/// </summary>
WriteExtraSingle = 49,
/// <summary>
/// Multiple block read type (ADTC, R1)
/// </summary>
ReadExtraMulti = 58,
/// <summary>
/// Multiple block write type (ADTC, R1)
/// </summary>
WriteExtraMulti = 59,
#endregion Class 11 SecureDigital Commands (Function Extension)
}
/// <summary>
/// SecureDigital application-specific commands
/// </summary>
public enum SecureDigitalCommands : byte
{
/// <summary>
/// Defines the data bus width to be used for data transfer (AC, R1)
/// </summary>
SetBusWidth = 6,
/// <summary>
/// Sends the SD status register (ADTC, R1)
/// </summary>
SendStatus = 13,
/// <summary>
/// Send the number of the written write blocks (ADTC, R1)
/// </summary>
SendNumWriteBlocks = 22,
/// <summary>
/// Set the number of write blocks to be pre-erased before writing (AC, R1)
/// </summary>
SetWriteBlockEraseCount = 23,
/// <summary>
/// Sends host capacity support information and asks the card to send its operating condition register (BCR, R3)
/// </summary>
SendOperatingCondition = 41,
/// <summary>
/// Connects/Disconnects the 50 kOhm pull-up resistor on CD/DAT3 pin of card (AC, R1)
/// </summary>
SetClearCardDetect = 42,
/// <summary>
/// Reads the SD Configuration Register SCR (ADTC, R1)
/// </summary>
SendSCR = 51,
}
[Flags]
public enum MmcFlags : uint
{
ResponsePresent = 1 << 0,
Response136 = 1 << 1,
ResponseCrc = 1 << 2,
ResponseBusy = 1 << 3,
ResponseOpcode = 1 << 4,
CommandMask = 3 << 5,
CommandAC = 0 << 5,
CommandADTC = 1 << 5,
CommandBC = 2 << 5,
CommandBCR = 3 << 5,
ResponseSPI_S1 = 1 << 7,
ResponseSPI_S2 = 1 << 8,
ResponseSPI_B4 = 1 << 9,
ResponseSPI_Busy = 1 << 10,
ResponseNone = 0,
Response_R1 = ResponsePresent | ResponseCrc | ResponseOpcode,
Response_R1b = ResponsePresent | ResponseCrc | ResponseOpcode | ResponseBusy,
Response_R2 = ResponsePresent | Response136 | ResponseCrc,
Response_R3 = ResponsePresent,
Response_R4 = ResponsePresent,
Response_R5 = ResponsePresent | ResponseCrc | ResponseOpcode,
Response_R6 = ResponsePresent | ResponseCrc | ResponseOpcode,
Response_R7 = ResponsePresent | ResponseCrc | ResponseOpcode,
ResponseSPI_R1 = ResponseSPI_S1,
ResponseSPI_R1b = ResponseSPI_S1 | ResponseSPI_Busy,
ResponseSPI_R2 = ResponseSPI_S1 | ResponseSPI_S2,
ResponseSPI_R3 = ResponseSPI_S1 | ResponseSPI_B4,
ResponseSPI_R4 = ResponseSPI_S1 | ResponseSPI_B4,
ResponseSPI_R5 = ResponseSPI_S1 | ResponseSPI_S2,
ResponseSPI_R7 = ResponseSPI_S1 | ResponseSPI_B4
}
}

View File

@@ -328,6 +328,71 @@ namespace DiscImageChef.Devices.Linux
return error;
}
/// <summary>
/// Sends a MMC/SD command
/// </summary>
/// <returns>The result of the command.</returns>
/// <param name="fd">File handle</param>
/// <param name="command">MMC/SD opcode</param>
/// <param name="buffer">Buffer for MMC/SD command response</param>
/// <param name="timeout">Timeout in seconds</param>
/// <param name="duration">Time it took to execute the command in milliseconds</param>
/// <param name="sense"><c>True</c> if MMC/SD returned non-OK status</param>
/// <param name="write"><c>True</c> if data is sent from host to card</param>
/// <param name="isApplication"><c>True</c> if command should be preceded with CMD55</param>
/// <param name="flags">Flags indicating kind and place of response</param>
/// <param name="blocks">How many blocks to transfer</param>
/// <param name="argument">Command argument</param>
/// <param name="response">Response registers</param>
/// <param name="blockSize">Size of block in bytes</param>
internal static int SendMmcCommand(int fd, MmcCommands command, bool write, bool isApplication, MmcFlags flags, uint argument, uint blockSize, uint blocks, ref byte[] buffer, out uint[] response, out double duration, out bool sense, uint timeout = 0)
{
response = null;
duration = 0;
sense = false;
if(buffer == null)
return -1;
mmc_ioc_cmd io_cmd = new mmc_ioc_cmd();
IntPtr bufPtr = Marshal.AllocHGlobal(buffer.Length);
io_cmd.write_flag = write;
io_cmd.is_ascmd = isApplication;
io_cmd.opcode = (uint)command;
io_cmd.arg = argument;
io_cmd.flags = flags;
io_cmd.blksz = blockSize;
io_cmd.blksz = blocks;
if(timeout > 0)
{
io_cmd.data_timeout_ns = timeout * 1000000000;
io_cmd.cmd_timeout_ms = timeout * 1000;
}
io_cmd.data_ptr = (ulong)bufPtr;
Marshal.Copy(buffer, 0, bufPtr, buffer.Length);
DateTime start = DateTime.UtcNow;
int error = Extern.ioctlMmc(fd, LinuxIoctl.MMC_IOC_CMD, ref io_cmd);
DateTime end = DateTime.UtcNow;
sense |= error < 0;
if(error < 0)
error = Marshal.GetLastWin32Error();
Marshal.Copy(bufPtr, buffer, 0, buffer.Length);
response = io_cmd.response;
duration = (end - start).TotalMilliseconds;
Marshal.FreeHGlobal(bufPtr);
return error;
}
public static string ReadLink(string path)
{
IntPtr buf = Marshal.AllocHGlobal(4096);

View File

@@ -145,6 +145,8 @@ namespace DiscImageChef.Devices.Linux
// SCSI IOCtls
SG_GET_VERSION_NUM = 0x2282,
SG_IO = 0x2285,
// MMC IOCtl
MMC_IOC_CMD = 0xC048B300
}
[Flags]

View File

@@ -52,6 +52,9 @@ namespace DiscImageChef.Devices.Linux
[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)]
internal static extern int ioctlSg(int fd, LinuxIoctl request, ref sg_io_hdr_t value);
[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)]
internal static extern int ioctlMmc(int fd, LinuxIoctl request, ref mmc_ioc_cmd value);
[DllImport("libc", CharSet = CharSet.Ansi, SetLastError = true)]
internal static extern int readlink(string path, System.IntPtr buf, int bufsize);

View File

@@ -65,5 +65,62 @@ namespace DiscImageChef.Devices.Linux
public uint duration; /* [o] */
public SgInfo info; /* [o] */
}
[StructLayout(LayoutKind.Sequential)]
struct mmc_ioc_cmd
{
/// <summary>
/// Implies direction of data. true = write, false = read
/// </summary>
public bool write_flag;
/// <summary>
/// Application-specific command. true = precede with CMD55
/// </summary>
public bool is_ascmd;
public uint opcode;
public uint arg;
/// <summary>
/// CMD response
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public uint[] response;
public MmcFlags flags;
public uint blksz;
public uint blocks;
/// <summary>
/// Sleep at least <see cref="postsleep_min_us"/> useconds, and at most
/// <see cref="postsleep_max_us"/> useconds *after* issuing command.Needed for
/// some read commands for which cards have no other way of indicating
/// they're ready for the next command (i.e. there is no equivalent of
/// a "busy" indicator for read operations).
/// </summary>
public uint postsleep_min_us;
/// <summary>
/// Sleep at least <see cref="postsleep_min_us"/> useconds, and at most
/// <see cref="postsleep_max_us"/> useconds *after* issuing command.Needed for
/// some read commands for which cards have no other way of indicating
/// they're ready for the next command (i.e. there is no equivalent of
/// a "busy" indicator for read operations).
/// </summary>
public uint postsleep_max_us;
/// <summary>
/// Override driver-computed timeouts.
/// </summary>
public uint data_timeout_ns;
/// <summary>
/// Override driver-computed timeouts.
/// </summary>
public uint cmd_timeout_ms;
/// <summary>
/// For 64-bit machines <see cref="data_ptr"/> , wants to
/// be 8-byte aligned.Make sure this struct is the same size when
/// built for 32-bit.
/// </summary>
public uint __pad;
/// <summary>
/// DAT buffer
/// </summary>
public ulong data_ptr;
}
}