30 November 2008

Read CID and CSD C# implementation.

Ok, here goes. It's pretty huge, so I'll just post the important bits and post a download link to the source at the bottom. I've not yet started on getting overlap to work. YOU HAVE TO SET THE BUILD TARGET TO x86. (if you have an express edition then you'll need to expose the configuration manager first - its in the settings somewhere - "show advanced build options" or something).

(Remember this is Windows XP/Vista, Admin privileges required, build target must be x86, SD card must be attached via an SD reader connected directly to the PCI host - no USB reader...)

First up...

ENUMS - Boring they are in the header files in the WDK

// SD_COMMAND_CLASS
public enum SdCommandClass : uint
{
    Standard,                       // SDCC_STANDARD
    AppCmd                          // SDCC_APP_CMD
};

// SD_TRANSFER_DIRECTION
public enum SdTransferDirection : uint
{
    Unspecified,                    // SDTD_UNSPECIFIED
    Read,                           // SDTD_READ
    Write                           // SDTD_WRITE
};

// SD_TRANSFER_TYPE
public enum SdTransferType : uint
{
    Unspecified,                    // SDTT_UNSPECIFIED
    CmdOnly,                        // SDTT_CMD_ONLY
    SingleBlock,                    // SDTT_SINGLE_BLOCK
    MultiBlock,                     // SDTT_MULTI_BLOCK
    MultiBlockNoCmd12               // SDTT_MULTI_BLOCK_NO_CMD12
};

// SD_RESPONSE_TYPE
public enum SdResponseType : uint
{
    Unspecified,                    // SDRT_UNSPECIFIED
    None,                           // SDRT_NONE
    R1,                             // SDRT_1
    R1b,                            // SDRT_1B
    R2,                             // SDRT_2
    R3,                             // SDRT_3
    R4,                             // SDRT_4
    R5,                             // SDRT_5
    R5b,                            // SDRT_5B
    R6                              // SDRT_6
};

// SFFDISK_DCMD
public enum SffdiskDcmd : uint
{
    GetVersion,                     // SFFDISK_DC_GET_VERSION
    LockChannel,                    // SFFDISK_DC_LOCK_CHANNEL
    UnlockChannel,                  // SFFDISK_DC_UNLOCK_CHANNEL
    DeviceCommand                   // SFFDISK_DC_DEVICE_COMMAND
};
public enum IoCtlCode : uint
{
    SffdiskQueryDeviceProtocol = 0x71E80,       // IOCTL_SFFDISK_QUERY_DEVICE_PROTOCOL   
    SffdiskDeviceCommand = 0x79E84,             // IOCTL_SFFDISK_DEVICE_COMMAND   
    VolumeGetVolumeDiskExtents = 0x560000       // IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
};

Native Methods - There are plenty of overrides of DeviceIOControl. The last one that takes byte[] sends the sd command. Note the use of SafeHandles instead of IntPtr.

[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
public extern static SafeFileHandle CreateFile(String fileName, AccessRights desiredAccess, ShareModes shareMode, IntPtr securityAttributes, CreationDisposition creationDisposition, int flagsAndAttributes, IntPtr hTemplateFile);

// overload used for querydeviceprotocol
[DllImport("kernel32", SetLastError = true)]
public extern static bool DeviceIoControl(SafeFileHandle hVol, IoCtlCode controlCode, IntPtr inBuffer, int inBufferSize, ref SffdiskQueryDeviceProtocolData outBuffer, int outBufferSize, out int bytesReturned, IntPtr overlapped);

// overload used for getting the disk extents
[DllImport("kernel32", SetLastError = true)]
public extern static bool DeviceIoControl(SafeFileHandle hVol, IoCtlCode controlCode, IntPtr inBuffer, int inBufferSize, out DiskExtents outBuffer, int outBufferSize, out int bytesReturned, IntPtr overlapped);

// overload used for getting more than 1 diskextent
[DllImport("kernel32", SetLastError = true)]
public extern static bool DeviceIoControl(SafeFileHandle hVol, IoCtlCode controlCode, IntPtr inBuffer,int inBufferSize, IntPtr outBuffer, int outBufferSize, out int bytesReturned, IntPtr overlapped);

// Overload for the CID
[DllImport("kernel32", SetLastError = true)]
public extern static bool DeviceIoControl(SafeFileHandle hVol, IoCtlCode controlCode, Byte[] inBuffer, int inBufferSize, Byte[] outBuffer, int outBufferSize, out int bytesReturned, IntPtr ovelapped);

The Structures - No fancy marshaling required. Again, see the WDK header files.

// SDCMD_DESCRIPTOR
[StructLayout(LayoutKind.Sequential)]
struct SdCmdDescriptor
{
    public Byte CommandCode;
    public SdCommandClass CmdClass;
    public SdTransferDirection TransferDirection;
    public SdTransferType TransferType;
    public SdResponseType ResponseType;
    public int GetSize()
    {
        return Marshal.SizeOf(this);
    }
}
// SFFDISK_DEVICE_COMMAND_DATA
[StructLayout(LayoutKind.Sequential)]
struct SffdiskDeviceCommandData
{
    public ushort Size;                     // 0
    public ushort Reserved;                 // 2
    public SffdiskDcmd Command;             // 4
    public ushort ProtocolArgumentSize;     // 8
    public uint DeviceDataBufferSize;       // 12
    public uint Information;                // 16   *ULONG_PTR*, Data[] Follows.
    public void Init()
    {
        this.Size = (ushort)Marshal.SizeOf(this);
    }
} 
// SFFDISK_QUERY_DEVICE_PROTOCOL_DATA
[StructLayout(LayoutKind.Sequential)]
struct SffdiskQueryDeviceProtocolData
{
    public ushort Size;
    public ushort Reserved;
    public Guid ProtocolGuid;
    public void Init()
    {
        this.Size = (ushort)Marshal.SizeOf(this);
    }
}

The class that makes the call - could do with more tidying up. CID and CSD are classes with lots of properties, the constructor takes the byte array returned by the deviceIOControl call and fills in the properties. They probably have mistakes too. See the linked code.

using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

namespace JDMcF.SDCard
{
    // This class might get some information about an SDCard.
    // Does not work with USB SD Card Readers.
    // Does not work with some SD Bus Host Drivers.
    // Administrator privileges required.
    // I don't know if the IOCTLs work with mobile devices.
    // I don't know if I'm translating the bytes from the CID correctly.
    public class SDCard
    {
        // GUID_SFF_PROTOCOL_SD
        private static readonly Guid GuidSffProtocolSd = new Guid("AD7536A8-D055-4C40-AA4D-96312DDB6B38");

        private CID cid;
        public CID CID { get { return cid; } }

        private CSD csd;
        public CSD CSD { get { return csd; } }

        private DriveInfo driveInfo;
        public DriveInfo DriveInfo { get { return driveInfo; } }

        private string physicalDrivePath;
        public string PhysicalDrivePath { get { return physicalDrivePath; } }

        private SDCard(string physicalDrivePath, DriveInfo driveInfo)
        {
            // At first only these are initialised. It takes a second or so to
            // read the CID so we so
            // it on another thread and raise an event once done.
            this.physicalDrivePath = physicalDrivePath;
            this.driveInfo = driveInfo;
        }

        public static List<SDCard> GetSDCards()
        {
            List<SDCard> cards = new List<SDCard>();
            // There are probably ways to mount the card elsewhere,
            // so it might miss those...
            foreach (DriveInfo di in System.IO.DriveInfo.GetDrives())
            {
                // Are all SD Cards Removable? I don't know!
                if (di.DriveType == DriveType.Removable)
                {
                    // We are enumerating volumes. Volumes can span physical
                    // disks. Work out how many physical disks are involved
                    // with each volume. (seems unlikely, but I just figured
                    // out how to do this so what the heck)...
                    List<string> physicalPaths =
                        VolumeInfo.GetPhysicalDriveStrings(di);
                    foreach (string physicalPath in physicalPaths)
                    {
                        if (IsSD(physicalPath))
                        {
                            cards.Add(new SDCard(physicalPath, di));
                        }
                    }
                }
            }
            return cards;
        }

        // Send IOCTL_SFFDISK_QUERY_DEVICE_PROTOCOL to see if the handle
        // belongs to an SD Card.
        private static bool IsSD(string physicalPath)
        {
            SafeFileHandle hVol = null;
            try
            {
                hVol = NativeMethods.CreateFile(physicalPath, AccessRights.GenericRead, ShareModes.FileShareRead  ShareModes.FileShareWrite,
IntPtr.Zero, CreationDisposition.OpenExisting, 0, IntPtr.Zero);
                if (hVol.IsInvalid)
                {
                    throw new Win32Exception("Couldn't CreateFile for " +
                                             physicalPath);
                SffdiskQueryDeviceProtocolData queryData1 =
                    new SffdiskQueryDeviceProtocolData();
                queryData1.Init();
                int bytesReturned;
                bool result = NativeMethods.DeviceIoControl(hVol, IoCtlCode.SffdiskQueryDeviceProtocol, IntPtr.Zero, 0, ref queryData1, queryData1.Size, out bytesReturned, IntPtr.Zero);
                return queryData1.ProtocolGuid.Equals(GuidSffProtocolSd);
            }
            finally
            {
                if (hVol != null)
                {
                    if (!hVol.IsInvalid)
                    {
                        hVol.Close();
                    }
                    hVol.Dispose();
                }
            }
        }

        public void RefreshData()
        {
            GetRegister(Register.CID);
            GetRegister(Register.CSD);
        }

        // Send the command in the array. On return the array will have any
        // response.
        private void SendCommand(byte[] command)
        {
            SafeFileHandle hVol = null;
            try
            {
                hVol = NativeMethods.CreateFile(PhysicalDrivePath, AccessRights.GenericRead  AccessRights.GenericWrite, ShareModes.FileShareRead  ShareModes.FileShareWrite, IntPtr.Zero, CreationDisposition.OpenExisting, 0, IntPtr.Zero);
                int bytesReturned;
                bool result = NativeMethods.DeviceIoControl(hVol, IoCtlCode.SffdiskDeviceCommand, command, command.Length, command, command.Length, out bytesReturned, IntPtr.Zero);
                if (!result) throw new Win32Exception();
            }
            finally
            {
                if (hVol != null)
                {
                    if (!hVol.IsInvalid)
                    {
                        hVol.Close();
                    }
                    hVol.Dispose();
                }
            }
        }

        // Get the CID or CSD. They are almost identical commands...
        private void GetRegister(Register register)
        {
            byte[] command = null;
            SffdiskDeviceCommandData commandData = new SffdiskDeviceCommandData();
            SdCmdDescriptor commandDescriptor = new SdCmdDescriptor();
            commandData.Init();
            commandData.Command = SffdiskDcmd.DeviceCommand;
            commandData.ProtocolArgumentSize = (ushort)commandDescriptor.GetSize();
            commandData.DeviceDataBufferSize = 16;
            commandDescriptor.CommandCode = (byte)register;   // <--- Not what the documentation indicates!
            commandDescriptor.CmdClass = SdCommandClass.Standard;
            commandDescriptor.TransferDirection = SdTransferDirection.Read;
            commandDescriptor.TransferType = SdTransferType.CmdOnly;
            commandDescriptor.ResponseType = SdResponseType.R2;

            // Now get the structs into the byte[]
            command = new byte[commandData.Size + commandData.ProtocolArgumentSize + commandData.DeviceDataBufferSize];
            IntPtr hBuf = Marshal.AllocHGlobal(command.Length);
            Marshal.StructureToPtr(commandData, hBuf, true);
            IntPtr descriptorOffset = new IntPtr(hBuf.ToInt32() + commandData.Size);
            Marshal.StructureToPtr(commandDescriptor, descriptorOffset, true);
            Marshal.Copy(hBuf, command, 0, command.Length);
            Marshal.FreeHGlobal(hBuf);

            SendCommand(command);
            // Strip out the return bytes that live at the end of the command byte array.
            byte[] regBytes = new byte[16];
            Buffer.BlockCopy(command, command.Length - 16, regBytes, 0, 16);

            if (register == Register.CID)
            {
                cid = new CID(regBytes);
            }
            else
            {
                csd = new CSD(regBytes);
            }
        }
    }
}

Formatting is awful...

16 comments:

  1. I tried this but it didn't work for me. Can you help me out please. I have a sony VAIO and the SD reader is built-in the system.

    Regards

    ReplyDelete
  2. Hi,

    I am getting error 50, "The request is not supported" in function IsSD() - any idea why? The SD I am interrogating is actually a GPS device connected via USB cable, and it mounts two Removable drives, G: and I:.

    I read the note about it not working unless it's connected to the PCI bus, could that be the reason? If yes, isn't that a little limited? And how can one go about getting the CID for any type of SD card, be it in built-in readers or USB readers?

    Many thanks,
    Michael.

    ReplyDelete
  3. Hi,

    The download link is failed. Could you please mail it to me? Thanks~~

    Schumi

    ReplyDelete
  4. Sorry, It's my network issue. I can download it now. Thanks.

    ReplyDelete
  5. can you pleas provide source code to download??

    ReplyDelete
  6. Hi.
    Is it impossible to get CID of SD card which is connected to USB reader?
    If so, are there no way to identify SD card physically?

    ReplyDelete
  7. I have tried this code and I've got this output:

    \\.\PhysicalDrive3 I:\
    --------------------
    Raw CID Bytes: 00-00-00-00-06-00-00-00-60-48-A4-6E-10-00-3F-00
    --------------------
    Manufacturer ID: 3f
    OEM ID:
    Product Name: nH`
    Product Revision: 0.0
    Product Serial Number: 00060000
    Manufacture Date: 0/2000
    --------------------
    Raw CSD Bytes: 00-00-00-00-06-00-00-00-60-48-A4-6E-10-00-3F-00
    --------------------
    CSD Version 2 bit value: CSD Version 1.0
    Data Read Access Time 1 (TAAC):
    Data Read Access Time 2 (NSAC): 16
    Max Data Transfer Rate: 0
    Card Command Classes: 101001000100
    Max Read Data Block Length: 8
    Partial Blocks For Read Allowed: False
    Write Block Misalignment: True
    Read Block Misalignment: True
    DSR Implemented: False
    Device Size: 0
    Max Read Current @ VDD Min: 0.5mA
    Max Read Current @ VDD Max: 1mA
    Max Write Current @ VDD Min: 0.5mA
    Max Write Current @ VDD Max: 1mA
    Device Size Multiplier: 2
    Erase Single Block Enable: False
    Erase Sector Size: 13
    Write Protect Group Size: 1
    Write Protect Group Enable: False
    Write Speed Factor: 1
    Max Write Data Block Length: 2^0
    Partial Blocks For Write Allowed: False
    File Format Group: False
    Copy Flag (OTP): False
    Permanent Write Protection: False
    Temporary Write Protection: False
    File Format: 0

    When I check on the SD bus I cannot see the CMD9 and CMD10 translated correctly.
    Anyone can help?
    MiKL~

    ReplyDelete
  8. Hi,

    I was writing program in C++ to read CID register. The procedure I have followed is but I am facing the same issue which I see that other people are also seeing.." I get 15 bytes of CID with a 00 in front and no CRC."
    Were you able to root cause the reason for the same. Even when reading the whole buffer, this value is not there itself.

    Regards,
    Rakendra

    ReplyDelete
  9. Hi could someone provide the source code. I don't seem to be able to find the link. Thanks.

    ReplyDelete
    Replies
    1. Thanks found the link... I'll report any success.

      Delete
  10. Hi!

    Looking for a solution to read extended CSD (512 bytes) of eMMC devices.
    CID / CSD can be accessed and in theory CMD8 (SEND_EXT_CSD) should work but no luck...

    Do you have any suggestions?

    Thanks so much in advance!!

    ReplyDelete
  11. The address link for the download is https://6zkatq.bl3301.livefilestore.com/y3mcs8fLISmDm5mpRPj_eqQ0_h_Sag4Nh2phG93llQDc35jy4Z5qkMQXUea2rTLBK3TmpRwCOv3tN42oA9g0ASiJZrY-RZfxm69zXIyzi1KubBGRxtECeLRTe2Nih4IXmMJ6qszQt90CgCfg05u3doYOg/JDMcF.SDCard.zip?download&psid=1

    The webpage seems to loose the link to the source when a new comment is added.

    ReplyDelete
    Replies
    1. http://dropbox.com/sh/og6qm14hq4r0cvk/AAB_Mu7gBt6rcZbGM-0oBmuCa?dl=0
      ReadCID.exe & JDMcF.SDCard-src.zip

      Got it from: https://onedrive.live.com/?id=862DEE3EC267CB5C

      Modded it so IsSD() always returns true.
      ... and added error message output for GetLastError, if DeviceIoControl fails.

      Attentions if you see error message at the beginning, the following will probably wrong!

      Well DeviceIoControl( SffdiskQueryDeviceProtocol ...) is the critical point, doesn't get it work with my
      PCI\VEN_104C&DEV_803B Texas Instruments PCIxx12 Integrated FlashMedia Controller
      [TiFm21.sys 2.0.10 2009]
      on Win7Sp1 X64

      However with the error messages you might get I tiny bit more a clue what is not working and how to fix it.

      Delete
    2. Some Appendix
      I found that SffdiskQueryDeviceProtocol = 0x71E80 Dword in \Windows\SYSTEM32\drivers\sffdisk.sys
      It's the 'SFF Storage Class Driver' (Small Form Factor Disk Driver) from Microsoft 6.1.7600.16385 (win7_rtm.090713-1255) from about 2009.

      I decompiled it with IDA ...
      ...
      if ( DeviceIoControlCMD == SffdiskQueryDeviceProtocol )
      {
      if ( *(input + 8) < 0x14u )
      goto STATUS_BUFFER_TOO_SMALL;
      Result1 = (*(Arg1_0x40 + 0xA0))(*(Arg1_0x40 + 0x98), 6i64, 20i64, *(a2 + 0x18));
      goto PreExit1;
      }
      ...
      and indeed thats sffdisk.sys is somehow involved on driver level to get DeviceIoControl( SffdiskQueryDeviceProtocol ...) and do something about it.

      But well when I put my SDCard into the reader and check via

      C:\>sc query sffdisk
      The status of sffdisk it says it's not running.

      Well if it is working on ya PC could you please check via the command above if sffdisk is running on ya PC if you've put in some card into the reader?
      TIA

      Delete
  12. Are you trying to earn money from your visitors by popup ads?
    If so, have you considered using exoClick?

    ReplyDelete