SD MMC and Windows CE

Lately there have been a lot of questions about SD and MMC in the newsgroups, especially about what is supported by the Microsoft SD bus driver. This blog post hopefully helps clear up some things about SD/MMC support in Windows CE.

First let’s look at an overview of the MMC and SD specifications and who supports what:

Specification overview
CE Version SD Spec MMC Spec
Win CE 5.0 RTM 1.1 3.x
Win CE 5.0 QFE (April 2007 onward) 2.0 4.3
Win Mobile 6.0 RTM 1.1 3.x
Win Mobile 6.x (AKU 0.2 onward) 2.0 4.3
Win CE 6.0 RTM 1.1 3.x
Win CE 6.0 R2 2.0 4.3

From the table above it looks like the SD bus driver in CE 6.0 R2 supports the MMC 4.3 specification, but actually it doesn’t completely (as we’ll see a bit later in this blog post).

The Microsoft SD bus driver (sdbus2.dll) is fully supporting the SD 2.0 specification. Because the MMC 4.3 specification is quite similar to SD 2.0, CE “somewhat” supports MMC 4.3.

So what does this mean? Well, for example, the number of data lines (bus width) of MMC supported by the Microsoft SD bus driver differs from the MMC specification:

8/4 or 1-bit mode

The MMC specification tells us it supports 8-bit wide bus mode (8-data lines). However, the SD 2.0 specification does not support 8 bit bus mode. Since CE officially supports SD but not MMC, CE does not support the 8-bit mode for any MMC/SD card. Unfortunately it appears the 4-bit mode is also not supported for MMC cards by the Microsoft SD bus driver even though the SD specification does support this mode. This leaves 1-bit mode as the only supported mode for MMC.

Here’s an overview of the supported modes by the SD bus driver (sdbus2.dll):

Bus mode SD MMC
1 Y Y
4 Y N
8 N N

So does this mean that MMC cannot support 4 or 8 bit at all? Well no, not really... You can always CLONE the SDBUS driver and modify it according to your needs or you can develop your own driver without using the SD bus driver at all.

High Capacity

According to the SD 2.0 specification "High Capacity" means cards with sizes larger then 2GB. SDHC cards are supported by the SD bus driver version 2.0. However, because of a bug in the SD bus driver high capacity MMC cards don’t work. You need to fix one line of code to support HC MMC cards. Besides this bug there is also a difference in the protocol for MMC and SD 2.0 regarding High Capacity, more about that later.

First let’s fix the obvious bug so that your High Capacity MMC card will be recognized and mounted properly:

The first step is to clone the SD Bus driver located at <WINCEROOT>\PUBLIC\COMMON\OAK\DRIVER\SDCARD\SDBUS

  1. Open a build release window
  2. Type "cd %_targetplatroot%"
  3. Type "cd src"
  4. Type "cd drivers"
  5. Type "md sdbus2"
  6. Type "cd sdbus2"
  7. Type "sysgen_capture -p common sdbus"
  8. Now copy the files from <WINCEROOT>\PUBLIC\COMMON\OAK\DRIVER\SDCARD\SDBUS into <WINCEROOT>\PLATFORM\<YourBSP>\SRC\DRIVERS\SDBUS2
  9. And merge sources.sdbus and sources so you end up with this sources file:
  10. SYNCHRONIZE_BLOCK=1

    TARGETNAME=sdbus
    TARGETDEFNAME=SDBus2
    DEFFILE=$(TARGETDEFNAME).def

    TARGETTYPE=DYNLINK
    RELEASETYPE=PLATFORM

    DLLENTRY=_DllEntryCRTStartup

    SOURCES = sdbusreq.cpp \
              sddevice.cpp \
              sdbus.cpp \
              sdslot.cpp \
              sdclient.cpp \
              sddevinf.cpp \
              sdiofeat.cpp \
              sdworki.cpp \
              sddebug.cpp \

    TARGETLIBS=                                           \
      $(_SYSGENOAKROOT)\lib\$(_CPUINDPATH)\defbuslib.lib  \
      $(_SYSGENSDKROOT)\lib\$(_CPUINDPATH)\coredll.lib    \
      $(_SYSGENOAKROOT)\lib\$(_CPUINDPATH)\ceddk.lib    

    Note that I added SYNCHRONIZE_BLOCK=1 to make sure any other component that will link to the sdbus library will be able to find it. SYNCHRONIZE_BLOCK=1 makes sure this folder is built before any other folder in the DRIVERS folder is built.

  11. Try to build the SDBUS2 driver; it should build without errors. If it doesn’t, make sure you selected the SD Bus Driver in the catalog and performed a sysgen on your OSDesign.
  12. And add the SDBUS2 folder to the dirs file in the DRIVERS folder.
  13. Don't forget to change any BSP component that links to $(_COMMONOAKROOT)\lib\$(_CPUINDPATH)\sdbus.lib to use the cloned SDBUS library, located at $(_TARGETPLATROOT)\lib\$(_CPUINDPATH)\sdbus.lib

Now change StringCchCopy to StringCchCat in line 1394 of sddevice.cpp (in your cloned folder of course!). This will append the "\\High_Capacity" string to the Client driver registry entry instead of replacing it (which leads to not finding the correct registry path for the profile because "HKLM\\High_Capacity" is not a valid registry path). Even with this fix it won’t find that path because Microsoft did not provide registry settings for High Capacity MMC. Let’s fix that by adding the following to platform.reg:

; SDHC Memory Storage class driver
[HKEY_LOCAL_MACHINE\Drivers\SDCARD\ClientDrivers\Class\MMC_Class\High_Capacity]
   "Dll"="SDMemory.dll"
   "Prefix"="DSK"
   "BlockTransferSize"=dword:40  ; send no more than 64 blocks of data per bus transfer
   ;"SingleBlockWrites"=dword:1  ; alternatively force the driver to use single block access
   ;"IdleTimeout"=dword:7D0      ; 2000 milliseconds
   ;"IdlePowerState"=dword:2     ; 0 == D0, 1 == D1, etc.
   ;"DisablePowerManagement"=""  ; if value present, then disable (remove value to enable)

   "Profile"="MMC"
   "IClass"=multi_sz:"{A4E7EDDA-E575-4252-9D6B-4195D48BB865}",
                     "{8DD679CE-8AB4-43c8-A14A-EA4963FAA715}"

Now you can Build and Sysgen your BSP by right clicking on your BSP node in the Solution Explorer and choosing Build and Sysgen. NEVER EVER click Build and Sysgen from the Build menu, please read the blog post http://www.guruce.com/blogpost/whattobuildwhen to understand why you should delete that option from the build menu. After the Build and Sysgen of the BSP perform a Copy files to release directory followed by a Make image and download your kernel to your device. When you now insert a high capacity MMC card it should be recognized and mounted correctly, well, almost! The size is most probably still reported wrong. This is because of a difference in the protocol. CMD 8 is handled differently in the SD 2.0 and MMC 4.3 specification:

COMMAND 8 (CMD8)
Command Type Response Abbreviation Command Description
CMD8 MMC R1 SEND_EXT_CSD The card sends its EXT_CSD register as a Block of data.
CMD8 SD R7 SEND_IF_COND Sends SD Memory Card interface condition

In the SD protocol CMD8 is used to identify high capacity cards and it is send at the beginning of the initialization process. When CMD8 is getting a response it means that it is identified as a High Capacity card. If sending a CMD8 results in a response timeout it is not a High Capacity card.

For MMC the CMD8 means retrieving the density of the card from the EXT_CSD register instead of the CDS register. CMD8 must be send after a CMD7 command which will place the card into "Tran" state. Since the SD bus driver doesn't do that the CMD8 will time out.

Like I said the SD bus driver fully supports the SD 2.0 specification and it reads the card density from the CSD register. This works for SD 2.0, but not for MMC. To fully support High Capacity MMC cards you should modify the SD bus driver so it can handle CMD8 instead of CMD9 to retrieve the card density for MMC high capacity cards.

SDBUS or SDBUS2

Now how do you select the correct SD bus driver? First remember that you have to install the correct version of Windows CE and install all required QFEs (see specification overview above). For Windows Embedded CE 6.0 there are two catalog components for SDBUS; "SD Bus Legacy" and "SD Bus driver". The legacy one is the SD bus driver that supports SD specification 1.1 and the "SD Bus Driver" item supports the 2.0 specification.

For all other versions of CE there is only one catalog component which automatically selects the SD bus 1.1 specification. To support the 2.0 specification in CE versions prior to CE 6.0 you have to set the IMGSDBUS2 environment variable to 1.

Conclusion

Windows CE fully supports the SD 2.0 specification, but as we saw this doesn’t automatically mean it also fully supports High Capacity MMC cards. Fortunately with a couple modifications here and there you can now fully support High Capacity MMC cards as well!

Comments

Another common request is how we can support booting or updating the OS image from a SD card.
One of the optional components of Windows Embedded CE 6.0 SR2 is the VOIP_PXA270 BSP. This BSP includes a bootloader that is able to boot from SD. The code, as any other bootloader, is quite hardware dependent, but you may take the SD implementation from it and change the "PDD" to adapt it to your SD controller.

First, thanks for all kinds of information!

I crunched at step 10. Even though I have "Treat warnings as errors" turned off (NO on the properts dialog), it is still doing it and I get a boat load of warnings in the build. I previously had been building with the SD Bus Controller and didn't get these warnings. 1 or 2 in each header includded in each of the source files.

Did I miss something along the way?

TIA

So, if I understand correctly your build is failing because you get a couple of warnings and WARNISERROR is set to 1?

If that's the case, make sure your platform sources.cmn file (in your BSP's root) doesn't have WARNISERROR set to 1 or set WARNISERROR=0 in the SDBUS sources file.

Maybe post exactly what warnings/errors you get here, so we can help you better...

This is a portion of the dump.

C:\WINCE600\PLATFORM\CompuconBSP\src\DRIVERS\sdbus2\sources
Starting Build: set WINCEREL=1&&build&&makeimg
==============
BUILD: [Thrd:Sequence:Type ] Message
BUILD: [00:0000000000:PROGC ] Build started with parameters:
BUILD: [00:0000000001:PROGC ] Build started in directory: C:\WINCE600\PLATFORM\CompuconBSP\src\DRIVERS\sdbus2
BUILD: [00:0000000002:PROGC ] Checking for C:\WINCE600\sdk\bin\i386\srccheck.exe.
BUILD: [00:0000000003:PROGC ] Running passes WCEFILES0, MIDL, MC, ASN, THUNK, PRECOMPHEADER, COMPILE, LIB, LINK, MANAGEDRESX, MANAGEDMOD, MANAGEDDLL, MANAGEDEXE, MANAGEDWIN for ARM.
BUILD: [00:0000000004:PROGC ] Loading database "C:\WINCE600\PLATFORM\CompuconBSP\Build.dat".
BUILD: [00:0000000005:PROGC ] Done.
BUILD: [00:0000000006:PROGC ] Computing include file dependencies:
BUILD: [00:0000000007:PROGC ] Checking for SDK include directory: C:\WINCE600\sdk\CE\inc.
BUILD: [00:0000000008:PROGC ] Scan C:\WINCE600\PLATFORM\CompuconBSP\src\DRIVERS\sdbus2\
BUILD: [00:0000000018:PROGC ] Building COMPILE Pass in C:\WINCE600\PLATFORM\CompuconBSP\src\DRIVERS\sdbus2\ directory.
BUILD: [01:0000000028:PROGC ] Compiling .\sdbusreq.cpp
BUILD: [01:0000000031:ERRORE] C:\WINCE600\public\common\sdk\inc\linklist.h(98) : error C2220: warning treated as error - no 'object' file generated
BUILD: [01:0000000032:WARNN ] C:\WINCE600\public\common\sdk\inc\linklist.h(98) : warning C4127: conditional expression is constant
BUILD: [01:0000000033:WARNN ] c:\wince600\public\common\ddk\inc\SDCard.h(209) : warning C4201: nonstandard extension used : nameless struct/union
BUILD: [01:0000000034:WARNN ] C:\WINCE600\public\common\oak\inc\defbus.h(129) : warning C4512: 'DeviceFolder' : assignment operator could not be generated
BUILD: [01:0000000036:WARNN ] C:\WINCE600\public\common\oak\inc\defbus.h(162) : warning C4100: 'lpChildName' : unreferenced formal parameter
...

The errors even appear to be somewhat bogus. The first warning references this line

93:PLIST_ENTRY
94:RemoveTailList(PLIST_ENTRY ListHead)
95:{
96: PLIST_ENTRY _Tail_Entry;
97: _Tail_Entry = ListHead->Blink;
>98: RemoveEntryList(_Tail_Entry);
99: return _Tail_Entry;
100:}

BUILD: [01:0000000031:ERRORE] C:\WINCE600\public\common\sdk\inc\linklist.h(98) : error C2220: warning treated as error - no 'object' file generated
BUILD: [01:0000000032:WARNN ] C:\WINCE600\public\common\sdk\inc\linklist.h(98) : warning C4127: conditional expression is constant

If you need the entire capture, I can post it. It just didn't seem appropriate for a comments section.

Thanks again.

Dan

Hi Dan,

[I've edited your message a bit smaller]

The major problem here is that you have WARNISERROR set to 1. All these warnings can be ignored, and as far as being bogus; take a look at the definition of RemoveEntryList:

#define RemoveEntryList(Entry) do {\
PLIST_ENTRY _EX_Entry;\
_EX_Entry = (Entry);\
_EX_Entry->Blink->Flink = _EX_Entry->Flink;\
_EX_Entry->Flink->Blink = _EX_Entry->Blink;\
} while(0);

You see the while(0)? That's the warning. The do {} while(0); is used for easy break out on error, so also here the warning is real but can be ignored.

Just add WARNISERROR=0 to the sources file of SDBUS and all should be fine.

Tutorial on booting from SD would be greatly appreciated.

Thanks

Hi,

Thats a good article. I am trying to read the extended CSD register (512 bytes) using cmd "SEND_EXT_CSD" but i am not able to. I have also tried as you said, sending CMD7 before CMD8 but after CMD7 device is not responding to any command. Please suggest ?

Thanks

Hi Salim,

Thanks for you comments. I downloaded the MMC specification 4.3 (JESD84-A43.pdf available at http://www.jedec.org) and it notes that SEND_EXT_CSD (CMD8)is not supported for cards which implements the standard earlier then 4.0 (Section A.8.2). So perhaps you have older card? Have you tried cards which implemented 4.0 or higher?

Yes, I am using the same datasheet and i read the version of the card, it is 4.1. Moreover i am able to send the SWITCH command to enable the 4 bit mode but i am not able to readthe EXT_CSD. If i send the CMD7 card is hanging.

Thanks

Difficult to sense whats going on here. CMD7 'hanging' should worry me cause every command should at least 'time-out' when not implemented or not supported. It could also mean that the card is in different state then expected ( 7.5: Reception of CMD7 with card’s own relative address while the card is in Programming State is
ignored by the card and may be treated as an Illegal Command). Check the state prior sending CMD7 with the SEND_STATUS command (CMD13) and determine the card status...

Hi,

thank you very much for that very good article.

However I do have some questions.

In our setup we use the SD 3.0 spec compatible bus driver, i.e. Catalog item "SD Bus Driver" (SYSGEN_SDBUS, IMGSDBUS2).

I have followed your steps and I am able to build the dll.

But none of my changes make it into the final image.
Looking into CE.Bib and COMMON.BIB I noticed that SDBUS.DLL can either be "SDBUS2.DLL" or "SDBUS.DLL" depending on IMGSDBUS2 being set or not.
My assumption is that my freshly build SDBUS.dll is excluded in favour of the SDBUS2.DLL.

Question: using your approach, do I have to select "Legacy SD BUS Driver" from the catalog to include my cloned version?

Maybe I am missing something here.

TIA,

Bjoern

This blog post was written with Windows CE 5.0 in mind. In Windows CE 6.0 the sources file has changed, so you'll have to clone and change the sources file accordingly but not copy it straight from the blog post.

In short; you'll have to make sure the TARGETNAME is set to SDBUS2 to get an output of SDBUS2.DLL.

Hi Michel,

thanks for the clarification. I have changed the SOURCES file and I am up and running now.

Thanks again,

Bjoern

That is a great write-up. I'm wanting to browse the prolonged CSD sign-up (512 bytes) making use of cmd "SEND_EXT_CSD" however i 'm unable to. We have furthermore attempted when you stated, mailing CMD7 just before CMD8 yet following CMD7 gadget just isn't answering any kind of control. Make sure you recommend ?

It looks like you have the same situation as Salim.
So just to verify; We are talking about MMC card which implements version 4.0 or higher right?

It depends in which state your are when issuing a CMD7 to which state you end up. Whenever you want to read the CSD register make sure your'e in the Stand-by state. When in the Stand-by State you can issue the CMD7 state which will transist you into the Transfer-State. When your'e in the Transfer-State you can issue CMD8 to receive the CSD register.

When issuing a CMD7 when you're in the Programming-State you will go to the Disconnect-State. I suspect thats what going on in your case, but to verify that make sure you read in which state you are before sending CMD7.

Check the state diagram in the MMC spec (JESD84-A43.pdf) Figure 22 — MultiMediaCard state diagram (data transfer mode) and the note below it.

Let me know it this is the case,

Very informative article, thank you very much. I am using Windows CE 6.0 and when I look in the catalog I enable the SD Bus Driver, and then under SDIO\SDIO Host I enable the SDIO Standard Host Controller, and have also enabled the SD Memory item under SDIO Memory. Is this the correct configuration for an eMMC device?

Yes, Also check whether the SD driver in the BSP is enabled, assuming there is one ;-)