Blog

Error installing WEC2013 SDK: You must install Windows Embedded XAML Tools before you can proceed with this SDK installation

So, you've just updated your Visual Studio 2013 and WEC2013 installations and have generated an SDK for your OS Design. Now you install the SDK and you get this error message: "You must install Windows Embedded XAML Tools before you can proceed with this SDK installation."

Very annoying, and this is something that started being a problem with one of the later (post Update 5) updates for WEC2013. I did not have time to figure out exactly what is causing this issue (most probably some mismatching version information of the XAML toolset), but I did find a workaround:

Use Orca (or any other msi editor) to remove the Custom Action named "CA_CheckPreReqWEXTInstalled".

Orca is in the Microsoft Windows SDK which should be on your machine if you've installed Visual Studio. Take a look in your "Program Files (x86)" folder and search for "orca". You should find "Orca-x86_en_us.msi", or something similar (depending on what VS version you have installed). If you can't find anything make sure to install Visual Studio or download the Microsoft Windows SDK here.

Once you've installed Orca you can right click the SDK msi and choose "Edit with Orca". When Orca opens you'll have to select the "CustomAction" table on the left and select the row with action "CA_CheckPreReqWEXTInstalled":

Now press the delete key and a dialog should pop up asking you if you really want to permanently remove this row from the database:

Click OK, save the modified msi and exit Orca.

Now the SDK will install without problems.

DISCLAIMER: This is a workaround, not a final solution. I'm pretty sure it will all work just fine with the latest (mismatching version number) XAML tools, but I haven't checked it. Please let me know if you find any issues with this workaround by leaving a comment below, thanks!

What to build when in WEC2013

[This is an update of a blog post I wrote a long time ago. Check the comments under that post if you have any questions (or comment on this post if your question is not answered in the old post)]

A question that keeps coming back on the forums is "I changed some code, but it does not end up in my image", or "I changed some registry values in platform.reg, but if I look at the device registry, it's not there!", or even "Why does a kernel build take so long?".

The source of these problems is build-related. You've got to understand the build system in order to know exactly what to do. This blog post aims to give you a clear handle on "What to build when"!

At first glance, WEC2013 seems to only offer two build command: "Build Solution" and "Rebuild Solution". There are however a lot more variations that can save you a tremendous amount of time in your day-to-day work with Visual Studio 2013 and Platform Builder.

Before we dive into build system commands and how to use them I'd like to make sure you understand you should never modify code in the PUBLIC or PRIVATE trees. If you need to change functionality in those code trees it is very important you clone that code to your BSP folder or OS Design folder first. The main reason is that changing code in the PUBLIC or PRIVATE folder will not result in the code changes to be automatically build and included in your kernel image. You'd have to manually build that code, make sure the updated libraries, dlls and/or exes are in the right place, and then rebuild your kernel. The reason why this can cause many problems is because any WEC update may overwrite your code changes and maintenance is a nightmare.

So, keep this in mind:

Never change code in the PUBLIC or PRIVATE trees; always clone to your BSP or OS Design first!

For help with this process see the following blog posts:

Cloning public code: An example
Manual Clone of Public Code
Cloning CalibrUi in Windows CE 6.0 (this may need some updating for WEC2013 but it will give you an idea)

In all the versions before WEC2013 Microsoft included build commands in the Advanced Build menu that would execute "blddemo" without the "-q" parameter. This results in the ENTIRE WINCEX00 tree being built, thereby overwriting many libraries, DLLs and EXEs that shipped as binary with CE/EC. After years of protest by all the embedded MVPs we finally managed to convince MS that these commands should be erased from the IDE. However, you can of course still execute blddemo without -q on the command line. This command is, and always has been, very dangerous because sometimes the binaries do not match the actual source code. This can happen when an update updates some libraries, but not the source code. This *should* not happen, but it does happen over and over again. Executing "blddemo" without the "-q" parameter will result in your CE tree being in an undetermined state and the only solution is a complete uninstall and reinstall of CE/EC. So, if you're working from the command line; NEVER EVER execute blddemo without the -q parameter! If you're not working from the command line; no worries!

Phew! Now that we got that out of the way let's see what build command we've got to use in what situation:

WEC2013 Build Commands

  • Create a new OS Design (or add a new build configuration to an existing OS Design): Sysgen (Build Solution)
  • Since a new OS Design doesn't have anything in its build configuration's output folders (same with a new build configuration) you'll have to sysgen the entire solution (for that build configuration). The longest process, but luckily you don't have to do this many times (see below).

  • Change Platform Settings: Make image
  • Platform Settings
    If you change any Platform Settings (like IMGNOKITL, IMGNODEBUGGER, IMGPROFILER) all you have to do is a Make Image.

  • Change driver source code in your BSP: Build the driver and Make Image
  • If you change driver source code, all you have to do is just build the driver (WINCEREL must be set to 1 but it is set by default so unless you changed it there's no need to worry) and do a makeimg. If you only want to debug the driver you can also add the DLL to the Release Directory Modules list (menu Tools) and just restart the device (or reload the device driver on the device) without having to do a makeimg and download to device. Building just the driver is a simple right-click on the driver and Build in the IDE or "build" in the driver's folder on the command line.

  • Change multiple source code files in your BSP: Build the BSP and Make Image
  • The safe option, this way you can't forget to rebuild anything. Building the BSP is a simple right-click on the PLATFORM\BSP folder and Build in the IDE or "build" in the BSP's root folder on the command line.

  • Change platform.reg, bib, dat or db files in your BSP: Sysgen the BSP, Copy Files to Release Directory, Build All Subprojects and Make Image
  • A lot of steps, but this will still not take longer than a couple of minutes. If you change any of the platform.* files we need to re-filter (Sysgen) those files and make sure the filtered files are copied into the FLATRELEASEDIR (Copy Files to Release Directory). That last action did however clear the project binaries from the FLATRELEASEDIR so we need to make sure those binaries and settings are getting copied into the FLATRELEASEDIR again (Build All Subprojects) and finally we are ready to Make Image. Now your registry changes will be in the image (check reginit.ini to make sure, last entry wins).

  • Change some source files and platform.reg, bib, dat or db files in your BSP: Build and Sysgen the BSP, Copy Files to Release Directory, Build All Subprojects and Make Image
  • Only difference with previous situation is that you now have to build the BSP to include the source code changes.

    The "Build and Sysgen BSP" command can be executed by right clicking your BSP root node in the Solution Explorer and choosing "Build and Sysgen (cebuild -qbsp)". Notice the -q in that command? That shows you it's a good command...

  • Change the workspace configuration (add or delete a component): Sysgen the entire workspace (Build solution)
  • For most components a simple Sysgen is enough. For some components (like when changing from RAM based registry to Hive based Registry) a Clean Sysgen is needed. This action takes the longest (anywhere from 5 minutes for a small workspace configuration on a very fast machine to a couple of hours for a really big configuration and a very slow machine). A Sysgen is a right-click on the workspace, Advanced Build Commands->Sysgen in the IDE or "blddemo -q" on the command line.

  • After installing one or multiple WEC updates: Clean Sysgen the entire workspace (Rebuild solution)
  • To make sure all components are re-linked with the (possibly updated) libraries a Clean Sysgen is needed. This action takes the longest (anywhere from 5 minutes for a small workspace configuration on a very fast machine to a couple of hours for a really big configuration and a very slow machine). A Clean Sysgen can be performed by choosing Rebuild Solution from the build menu, or choosing Advanced Build Commands->Clean Sysgen in the IDE or "blddemo clean -q" on the command line.

Sometimes it's easier to build from the command line. If you are unsure what command to type you can always perform the action in the IDE first and watch the 3rd line in the build output window starting with "Starting Build:". Behind the colon is the exact command line for that action, eg Sysgen on the BSP: "Starting Build: SysgenPlatform %_TARGETPLATROOT% preproc&&SysgenPlatform %_TARGETPLATROOT% postproc", so on the command line you would type "SysgenPlatform %_TARGETPLATROOT% preproc" followed by enter and the 2nd command "SysgenPlatform %_TARGETPLATROOT% postproc" followed by enter.

If you use the commandline, make sure you never forget "-q" when running "blddemo"!

I hope this blog post will help you speed up your builds and lower your frustration with the build system!

PS. Also check out this tip (works for WEC2013 too) to further improve build speed if your BSP is using code from the PLATFORM\COMMON\SRC\SOC folders.

Good luck!

A template for debug messages

In all the drivers and applications we develop here at GuruCE, we always take the time to add plenty of logging because we know good logging will save us loads of time debugging our code (not that we ever have to debug code here of course... ;)).

Windows CE has a really great mechanism for debug logging called Debug Zones. This mechanism allows you to divide your debug messages into 16 zones and each of these zones can be turned on or off at run-time. This allows you to control the verbosity of your debug output and helps prevent cluttering of messages. Read this excellent blog post by Travis first before reading on: http://blogs.msdn.com/ce_base/archive/2006/12/18/debug-messages-and-debu...

To add debug logging using Debug Zones to your application or driver do the following:

1. Define your zones using the DEBUGZONE macro
1. Define a DBGPARAM structure and fill it
2. Call either DEBUGREGISTER or RETAILREGISTERZONES from your DllMain (driver) or WinMain (application)
3. Log messages using either DEBUGMSG or RETAILMSG

Sounds easy (and actually it is) but to make it more easy (and because we found ourselves doing the same thing over and over) we created a template we now use in all our driver and application code, even on projects targeted for Desktop Windows. Even though "Big" Windows doesn't have a "Debug Zone" mechanism you can still use the template. The big benefit of course being that your code is completely portable between desktop PC's and CE devices (and also MBS/UNICODE)! The only thing you can't do is switch debug zones at run-time, but you can still select which zones should be on or off at compile-time.

We have released these templates under GPL for the greater good.

Let us explain the template by walking through it; we'll start with DebugZones.cpp (the line numbers reflect the actual line numbers in the template files):

  1. #include <windows.h>
  2. #include "DebugZones.h"
  3.  
  4. // Do NOT change anything in this file.
  5. // See DebugZones.h for things you can change!
  6. /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
  7. extern "C" DBGPARAM dpCurSettings =
  8. {
  9.     __WMODULE__,
  10.     {
  11.         ZONE0_TEXT, ZONE1_TEXT, ZONE2_TEXT, ZONE3_TEXT,
  12.         ZONE4_TEXT, ZONE5_TEXT, ZONE6_TEXT, ZONE7_TEXT,
  13.         ZONE8_TEXT, ZONE9_TEXT, ZONE10_TEXT, ZONE11_TEXT,
  14.         ZONE12_TEXT, ZONE13_TEXT, ZONE14_TEXT, ZONE15_TEXT
  15.     },
  16. #ifndef DEBUG // IN RETAIL...
  17.     RETAILZONES
  18. #else //IN DEBUG...
  19.     DEBUGZONES
  20. #endif
  21. };

The first and most important thing to note is that you should not change anything in this file. You configure the template all through the header file, which we'll will be discussing later.

In this first part of DebugZones.cpp we simply define a global variable dpCurSettings of type DBGPARAM and fill the structure with defines from DebugZones.h. The __WMODULE__ corresponds to the "lpszName" member, the ZONEx_TEXT defines fill the "rglpszZones" array and finally RETAILZONES or DEBUGZONES (depending on whether you build a "debug" or "release" version of the code) corresponds to the "ulZoneMask" member of the DBGPARAM structure. The CE debug system uses this structure to get Debug Zone information from the module and to set or clear debug zones during execution of the module. When you use the template for code targeted at Desktop Windows this structure will only be used by the DEBUGMSG/RETAILMSG macros (they use the ulZoneMask member as a display condition).

  1. #ifndef UNDER_CE
  2.   #include <stdio.h>
  3.   void WINAPIV NKDbgPrintf(LPCTSTR lpszFmt, ...)
  4.   {
  5.       #define BUFFER_SIZE 1000
  6.       va_list argptr;
  7.       va_start(argptr, lpszFmt);
  8.       TCHAR szBuffer[BUFFER_SIZE];
  9.       _vstprintf_s(szBuffer, BUFFER_SIZE, lpszFmt, argptr);
  10.       OutputDebugString(szBuffer);
  11.       va_end(argptr);
  12.   }
  13. #endif

This code is only used when you target Desktop Windows. Only Windows CE defines NKDbgPrintf so for Desktop Windows we had to create our own implementation of this function to make the Debug Zones system portable. This function simply takes a format string followed by a variable number of parameters and transforms that into a formatted string and outputs it to the debug output window of Visual Studio. Note that the maximum length of the formatted string is BUFFER_SIZE characters.

  1. // Helper function for runtime conversion of char* to _T
  2. LPCTSTR to_T(LPCSTR pStr)
  3. {
  4.     #define MAX_CALLS   4
  5.     #define MAX_MSG     500
  6.  
  7.     static TCHAR buffer[MAX_CALLS][MAX_MSG];
  8.     static DWORD index = 0;
  9.     LPCTSTR pszRet = buffer[index];
  10.     size_t conv;
  11.  
  12.     #ifdef UNICODE
  13.       mbstowcs_s(&conv, buffer[index], MAX_MSG, pStr, MAX_MSG);
  14.     #else //ANSI
  15.       strcpy_s(buffer[index], MAX_MSG, pStr);
  16.     #endif
  17.  
  18.     index++;
  19.     if (MAX_CALLS==index)
  20.         index = 0;
  21.     return pszRet;
  22. }

This next bit of code is a helper function that solves a problem when you want your code to be portable between MBS and UNICODE. MBS is multi-byte string, also called ANSI string, and UNICODE is, well, UNICODE strings. Let's say you are compiling for UNICODE but want to display a multi-byte string using a function that uses a format string. The way to do that is to use %S instead of %s in the format string. When you use %s in code compiled for UNICODE the variable indicated by %s should be in UNICODE. When you compile for MBS the variable indicated by %s should be MBS. In other words, %s indicates the "native" string format. If you want to display a multi-byte string in code compiled for UNICODE you'd use %S, and if you want to display a UNICODE string in code compiled for MBS you'd use %S as well. In other words, %S indicates the "opposite" string format. And here lies the problem: What if you want to display a multi-byte string regardless of whether the code is compiled for MBS or UNICODE?

Take a look at the following code:

NKDbgPrintf(_T("Display a native format string \"%s\"\r\n"), _T("This is a native string format"));
NKDbgPrintf(_T("Display opposite string format \"%S\"\r\n"), "This is a multi-byte string");

Note that the _T macro is defined as L in UNICODE (making the following string a UNICODE string) and as nothing in MBS (leaving the following string a multi-byte string).

When compiled for UNICODE the above code works perfectly:

Display a native format string "This is a native string format"
Display opposite string format "This is a multi-byte string"

But now see what happens when you compile the above code for MBS:

Display a native format string "This is a native string format"

You only get the output of the first line! So what just happened? Well, the _vstprintf_s function in NKDbgPrintf expected the first argument to be of the opposite string format. Since you compiled for MBS this means it expects a UNICODE string. The _vstprintf_s function detects it is not and bails out, setting szBuffer to an empty string.

The to_T function solves this problem:

NKDbgPrintf(_T("Display a native format string \"%s\"\r\n"), _T("This is a native string format"));
NKDbgPrintf(_T("Display opposite string format \"%s\"\r\n"), to_T("This is a multi-byte string"));

When compiled for MBS the output is:

Display a native format string "This is a native string format"
Display opposite string format "This is a multi-byte string"

I can hear you say: So why don't you just use _T() in that 2nd call to NkDbgPrintf? And yes, you are right, in the above code that would work, but what if it's a variable pointing to a multi-byte string? You can't use _T() around a variable:

NKDbgPrintf(_T("IPAddress \"%s\"\r\n"), to_T(inet_ntoa(ipAddress)));

Line 67 defines the maximum number of times you can call this function before it will start overwriting its internal buffers. Increase this value if you want to use more than 4 parameters that need to be converted in one call. To understand this, see the following code and its output:

char* s1 = "String 1";
char* s2 = "String 2";
char* s3 = "String 3";
char* s4 = "String 4";
char* s5 = "String 5";
char* s6 = "String 6";

NKDbgPrintf(_T("%s %s %s %s %s %s\r\n"), to_T(s1), to_T(s2), to_T(s3), to_T(s4), to_T(s5), to_T(s6));

Output:

String 1 String 2 String 3 String 4 String 1 String 2



Let's move on to the real core of the template; DebugZones.h:

  1. #pragma once
  2. #include <windows.h>
  3.  
  4. // Change the following definitions:
  5. /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
  6. #define __MODULE__ "MyDriver"  // The module name
  7.  
  8. // Define this to issue a DEBUHCHK(FALSE) when printing an ERRMSG and/or WRNMSG
  9. //#define DEBUGCHK_ON_ERRMSG
  10. //#define DEBUGCHK_ON_WRNMSG
  11.  
  12. // Definitions for our debug zones                              
  13. #define ZONE0_TEXT          _T("Init")
  14. #define ZONE1_TEXT          _T("Function")
  15. #define ZONE2_TEXT          _T("Memory")
  16. #define ZONE3_TEXT          _T("")
  17. #define ZONE4_TEXT          _T("")
  18. #define ZONE5_TEXT          _T("")
  19. #define ZONE6_TEXT          _T("")
  20. #define ZONE7_TEXT          _T("")
  21. #define ZONE8_TEXT          _T("")
  22. #define ZONE9_TEXT          _T("")
  23. #define ZONE10_TEXT         _T("")
  24. #define ZONE11_TEXT         _T("")
  25. #define ZONE12_TEXT         _T("")
  26. #define ZONE13_TEXT         _T("Info")
  27. #define ZONE14_TEXT         _T("Warning")
  28. #define ZONE15_TEXT         _T("Error")
  29.  
  30. // These macros can be used as condition flags for LOGMSG
  31. #define ZONE_INIT           DEBUGZONE(0)
  32. #define ZONE_FUNCTION       DEBUGZONE(1)
  33. #define ZONE_MEMORY         DEBUGZONE(2)
  34. #define ZONE_3              DEBUGZONE(3)
  35. #define ZONE_4              DEBUGZONE(4)
  36. #define ZONE_5              DEBUGZONE(5)
  37. #define ZONE_6              DEBUGZONE(6)
  38. #define ZONE_7              DEBUGZONE(7)
  39. #define ZONE_8              DEBUGZONE(8)
  40. #define ZONE_9              DEBUGZONE(9)
  41. #define ZONE_10             DEBUGZONE(10)
  42. #define ZONE_11             DEBUGZONE(11)
  43. #define ZONE_12             DEBUGZONE(12)
  44. #define ZONE_INFO           DEBUGZONE(13)
  45. #define ZONE_WARNING        DEBUGZONE(14)
  46. #define ZONE_ERROR          DEBUGZONE(15)
  47.  
  48. // These macros can be used to indicate which zones to enable by default (see RETAILZONES and DEBUGZONES below)
  49. #define ZONEMASK_INIT       ZONEMASK(0)
  50. #define ZONEMASK_FUNCTION   ZONEMASK(1)
  51. #define ZONEMASK_MEMORY     ZONEMASK(2)
  52. #define ZONEMASK_3          ZONEMASK(3)
  53. #define ZONEMASK_4          ZONEMASK(4)
  54. #define ZONEMASK_5          ZONEMASK(5)
  55. #define ZONEMASK_6          ZONEMASK(6)
  56. #define ZONEMASK_7          ZONEMASK(7)
  57. #define ZONEMASK_8          ZONEMASK(8)
  58. #define ZONEMASK_9          ZONEMASK(9)
  59. #define ZONEMASK_10         ZONEMASK(10)
  60. #define ZONEMASK_11         ZONEMASK(11)
  61. #define ZONEMASK_12         ZONEMASK(12)
  62. #define ZONEMASK_INFO       ZONEMASK(13)
  63. #define ZONEMASK_WARNING    ZONEMASK(14)
  64. #define ZONEMASK_ERROR      ZONEMASK(15)
  65.  
  66. #define RETAILZONES     ZONEMASK_INIT | ZONEMASK_WARNING | ZONEMASK_ERROR
  67. #define DEBUGZONES      RETAILZONES | ZONEMASK_FUNCTION | ZONEMASK_MEMORY | ZONEMASK_INFO
  68.  
  69. // Define FULL_CONTEXT to show full context in debug messages:
  70. // "Module: [full path to file name:line number] Function>"
  71. //#define FULL_CONTEXT
  72. /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/

This is the part of the template you should change to match your driver or application. First, in line 33, change the __MODULE__ define to the name of your driver or application. If you are creating a flash driver you'd set this to "FLASH" for instance.

Uncomment line 36 and/or 37 to generate a DEBUGCHK whenever you use ERRMSG and/or WRNMSG. You'd usually only uncomment these lines when you are in the development stage (when you're still actively debugging your code).

Next, in lines 40 to 55, you define the friendly names of the debug zones you want. These names will show up when you use the Debug Zones dialog or the "zo" command from Platform Builder. The predefined ones are always useful:

  • Init
  • Use this zone to display debug messages during the initialization phase of your driver. I use this zone in the Init function of drivers.

  • Function
  • This zone is used to display function entry and exit. Used by the FUNC_ENTRY and FUNC_EXIT macros (I'll discuss those later)

  • Memory
  • This zone is used to display memory allocation and de-allocation. Having a separate zone for memory really helps in identifying memory leaks or memory related problems in your driver or application (besides CeDebugX)

  • Info
  • This zone is used to display general information. I use this zone a lot through-out my code and enabling this zone usually means I get heaps of debug messages, but also heaps of information about the flow of my code

  • Warning
  • Use this zone for non-fatal errors (also known as warnings ;o)

  • Error
  • Use this zone for fatal errors; situations where you had to abort a certain operation

Another zone I use often is "IOCTL". If my driver handles custom IOCTLs I use that zone to display information when those IOCTLs are handled. If you want to add the IOCTL zone you'd change line 43 to:

  1. #define ZONE3_TEXT          _T("IOCTL")

Lines 58 to 73 define the actual zone IDs. You use these defines as the first parameter of LOG_MSG (that I'll discuss later) to indicate the zone this message belongs to. After you added the IOCTL zone to the friendly names list, you'd change line 61 to:

  1. #define ZONE_IOCTL          DEBUGZONE(3)

Lines 76 to 91 define the zone masks. You use these defines to indicate which zones should be enabled by default. After you added the IOCTL zone to the zone ID list, you'd change line 79 to:

  1. #define ZONEMASK_IOCTL      ZONEMASK(3)

Line 93 and 94 define the default zones to use in a release build and a debug build. I enable zones INIT, WARNING and ERROR in a release build and add FUNCTION, MEMORY and INFO to debug builds. Note that in a "ship" build (on Windows CE, WINCESHIP=1) debug/retail messages are defined as "nothing" (so all the logging code is effectively removed from the final binary).

If you define FULL_CONTEXT (line 98) the LOGMSG macro will output the full path to the source code filename in front of the message, instead of just the module name. We don't enable this by default because it makes the debug output cluttered and less readable (but it can be very handy in some cases).

This concludes the part of DebugZones.h that you should change. The remainder of the code mostly deals with the differences between Desktop Windows and Windows CE:

  1. // Do NOT change any of the following definitions:
  2. /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
  3. LPCTSTR to_T(LPCSTR pStr);
  4.  
  5. #ifdef UNDER_CE
  6.   #ifndef DEBUG
  7.     #ifdef __cplusplus
  8.       extern "C" DBGPARAM dpCurSettings;
  9.     #else //!__cplusplus
  10.       extern DBGPARAM dpCurSettings;
  11.     #endif __cplusplus
  12.   #endif DEBUG
  13. #else // !UNDER_CE
  14.     #include <tchar.h>
  15.     #include <stdio.h>
  16.  
  17.     typedef struct _DBGPARAM {
  18.             TCHAR       lpszName[32];           // @field Name of module
  19.             TCHAR   rglpszZones[16][32];    // @field names of zones for first 16 bits
  20.             ULONG   ulZoneMask;             // @field Current zone Mask
  21.     } DBGPARAM, *LPDBGPARAM;
  22.  
  23.     #ifndef DEBUG
  24.       #ifdef _DEBUG
  25.         #define DEBUG
  26.       #endif _DEBUG
  27.     #endif DEBUG
  28.  
  29.     extern DBGPARAM dpCurSettings;
  30.     void WINAPIV NKDbgPrintf(LPCTSTR lpszFmt, ...);
  31.  
  32.     #define RETAILMSG(cond,printf_exp)   \
  33.        ((cond)?(NKDbgPrintf printf_exp),1:0)
  34.  
  35.     #define DEBUGZONE(n)  (dpCurSettings.ulZoneMask & (0x00000001<<(n)))
  36.  
  37.     #define RETAILREGISTERZONES(hMod)
  38.  
  39.     #ifdef DEBUG
  40.         #define DEBUGMSG(cond,printf_exp)   \
  41.            ((void)((cond)?(NKDbgPrintf printf_exp),1:0))
  42.        
  43.         #define DBGCHK(module,exp) \
  44.            ((void)((exp)?1:(          \
  45.                NKDbgPrintf ( _T("%s: DEBUGCHK failed in file %s at line %d \r\n"), \
  46.                          module, _T(__FILE__) ,__LINE__ ),    \
  47.                DebugBreak(), \
  48.                    0  \
  49.            )))
  50.  
  51.         #define DEBUGCHK(exp) DBGCHK(dpCurSettings.lpszName, exp)
  52.         #define DEBUGREGISTER(hMod)
  53.     #endif DEBUG
  54. #endif
  55.  
  56. #ifndef __FUNCTION__
  57. #define __FUNCTION__ __FILE__
  58. #endif __FUNCTION__
  59.  
  60. #ifdef UNICODE
  61.   #define PREPW(x) L ## x
  62. #else //!UNICODE
  63.   #define PREPW(x) x
  64. #endif UNICODE
  65.  
  66. #define TOWSTR(x) PREPW(x)
  67. #ifndef __WFUNCTION__
  68. #define __WFUNCTION__ TOWSTR(__FUNCTION__)
  69. #endif __WFUNCTION__
  70. #define __WMODULE__ TOWSTR(__MODULE__)
  71. #ifndef __WFILE__
  72. #define __WFILE__ TOWSTR(__FILE__)
  73. #endif __WFILE__
  74.  
  75. #define ZONEMASK(n) (0x00000001<<(n))
  76.  
  77. #ifdef FULL_CONTEXT
  78.   #define FUNC_ENTRY(fmt, ...)    RETAILMSG(ZONE_FUNCTION, (__WMODULE__ _T(": +[") __WFILE__ _T(":%d] ") __WFUNCTION__ _T("(") fmt _T(")+\r\n"), __LINE__, __VA_ARGS__))
  79.   #define FUNC_EXIT(fmt, ...)     RETAILMSG(ZONE_FUNCTION, (__WMODULE__ _T(": -[") __WFILE__ _T(":%d] ") __WFUNCTION__ _T("(") fmt _T(")-\r\n"), __LINE__, __VA_ARGS__))
  80.   #define LOGMSG(cond, fmt, ...)  RETAILMSG(cond, (__WMODULE__ _T(":  [") __WFILE__ _T(":%d] ") __WFUNCTION__ _T("> ") fmt, __LINE__, __VA_ARGS__))
  81.   #ifdef DEBUGCHK_ON_ERRMSG
  82.     #define WRNMSG(fmt, ...)        {RETAILMSG(ZONE_WARNING, (__WMODULE__ _T(":  [") __WFILE__ _T(":%d] ") __WFUNCTION__ _T("> WARNING: ") fmt, __LINE__, __VA_ARGS__)); DEBUGCHK(FALSE);}
  83.     #define ERRMSG(fmt, ...)        {RETAILMSG(ZONE_ERROR, (__WMODULE__ _T(":  [") __WFILE__ _T(":%d] ") __WFUNCTION__ _T("> ERROR: ") fmt, __LINE__, __VA_ARGS__)); DEBUGCHK(FALSE);}
  84.   #else // !DEBUGCHK_ON_ERRMSG
  85.     #define WRNMSG(fmt, ...)        RETAILMSG(ZONE_WARNING, (__WMODULE__ _T(":  [") __WFILE__ _T(":%d] ") __WFUNCTION__ _T("> WARNING: ") fmt, __LINE__, __VA_ARGS__))
  86.     #define ERRMSG(fmt, ...)        RETAILMSG(ZONE_ERROR, (__WMODULE__ _T(":  [") __WFILE__ _T(":%d] ") __WFUNCTION__ _T("> ERROR: ") fmt, __LINE__, __VA_ARGS__))
  87.   #endif
  88. #else
  89.   #define FUNC_ENTRY(fmt, ...)    RETAILMSG(ZONE_FUNCTION, (__WMODULE__ _T(": +") __WFUNCTION__ _T("(") fmt _T(")+\r\n"), __VA_ARGS__))
  90.   #define FUNC_EXIT(fmt, ...)     RETAILMSG(ZONE_FUNCTION, (__WMODULE__ _T(": -") __WFUNCTION__ _T("(") fmt _T(")-\r\n"), __VA_ARGS__))
  91.   #define LOGMSG(cond, fmt, ...)  RETAILMSG(cond, (__WMODULE__ _T(":  ") __WFUNCTION__ _T("> ") fmt, __VA_ARGS__))
  92.   #ifdef DEBUGCHK_ON_ERRMSG
  93.     #define WRNMSG(fmt, ...)        {RETAILMSG(ZONE_WARNING, (__WMODULE__ _T(":  ") __WFUNCTION__ _T("> WARNING: ") fmt, __VA_ARGS__)); DEBUGCHK(FALSE);}
  94.     #define ERRMSG(fmt, ...)        {RETAILMSG(ZONE_ERROR, (__WMODULE__ _T(":  ") __WFUNCTION__ _T("> ERROR: ") fmt, __VA_ARGS__)); DEBUGCHK(FALSE);}
  95.   #else // !DEBUGCHK_ON_ERRMSG
  96.     #define WRNMSG(fmt, ...)        RETAILMSG(ZONE_WARNING, (__WMODULE__ _T(":  ") __WFUNCTION__ _T("> WARNING: ") fmt, __VA_ARGS__))
  97.     #define ERRMSG(fmt, ...)        RETAILMSG(ZONE_ERROR, (__WMODULE__ _T(":  ") __WFUNCTION__ _T("> ERROR: ") fmt, __VA_ARGS__))
  98.   #endif
  99. #endif
  100. /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/

The interesting part is from line 190 to 199 where we define a couple of macros that allow you to log messages in a good and consistent format:

  • FUNC_ENTRY
  • Usage:

    FUNC_ENTRY(format_string [, arg1, arg2, ...]);

    Examples:

    FUNC_ENTRY(_T("void"));
    FUNC_ENTRY(_T("lpParameter = 0x%08X"), lpParameter);

    Output:
    (Note the '+' characters that indicate function entry)

    ModuleName: +FunctionName(void)+
    ModuleName: +FunctionName(lpParameter = 0xA5A5A5A5)+
  • FUNC_EXIT
  • Usage:

    FUNC_EXIT(format_string [, arg1, arg2, ...]);

    Examples:

    FUNC_EXIT(_T(""));
    FUNC_EXIT(_T("return %s"), bRet ? _T("true") : _T("false"));

    Output:
    (Note the '-' characters that indicate function exit)

    ModuleName: -FunctionName()-
    ModuleName: -FunctionName(return false)-
  • LOGMSG
  • Usage:

    LOGMSG(ZONE_ID, format_string [, arg1, arg2, ...]);

    Examples:

    LOGMSG(ZONE_INIT, _T("Started successfully!\r\n"));
    LOGMSG(ZONE_INFO, _T("dwID = %d\r\n"), dwID);

    Output:

    ModuleName:  FunctionName> Started successfully!
    ModuleName:  FunctionName> dwID = 1234
  • ERRMSG
  • Examples:

    ERRMSG(_T("Invalid parameter (dwParam = %d)\r\n"), dwParam);

    Output:

    ModuleName:  FunctionName> ERROR: Invalid parameter (dwParam = 1234)
  • WRNMSG
  • Examples:

    WRNMSG(_T("Could not reach IP %s\r\n"), to_T(inet_ntoa(IPAddress)));

    Output:

    ModuleName:  FunctionName> WARNING: Could not reach IP 10.0.0.1

To use the Debug Zones template in your projects you simply add DebugZones.cpp and DebugZones.h to your solution and call RETAILREGISTERZONES(hMod) from your entry point function:

BOOL WINAPI DriverEntry(HANDLE hInstance, DWORD dwReason, LPVOID lpReserved)
{
    FUNC_ENTRY(_T("hInstance = 0x%08X, dwReason = 0x%08X"), hInstance, dwReason);

    switch(dwReason)
    {
    case DLL_PROCESS_ATTACH:
    {
        // Disable thread creation/deletion calls into this function
        DisableThreadLibraryCalls((HMODULE)hInstance);
        // Register with debug subsystem
        RETAILREGISTERZONES((HMODULE)hInstance);
        break;
    }
    case DLL_PROCESS_DETACH:
    {
        break;
    }
    default:
        break;
    }

    FUNC_EXIT(_T("TRUE"));
    return TRUE;
}

or in case of an application:

int WINAPI WinMain (HINSTANCE hInstance,
                    HINSTANCE hInstPrev,
                    LPWSTR pCmdLine,
                    int nCmdShow)
{
    // Register with debug subsystem
    RETAILREGISTERZONES(NULL);
    .
    .
    .

Note that the parameter to RETAIL/DEBUGREGISTERZONES MUST be NULL if you want to register debug zones from an application!

If you are using precompiled headers you have to set DebugZones.cpp to "Not Using Precompiled Headers" (in VS2008 right click DebugZones.cpp in your solution, click "Properties", then select "Precompiled Headers" under "Configuration Properties -> C/C++" and change the "Create/Use Precompiled Header" value). Now include "DebugZones.h" in any file you want to use debug zone logging.

The attached ZIP file contains the complete template.

I hope this template will prove as useful to you as it is to us!

Windows Embedded Compact May 2014 Update 35 breaks binary compatibility of Silverlight applications

Warning to everybody using Silverlight: the May 2014 Update 35 for Windows Embedded Compact 7 breaks binary compatibility for Silverlight applications!

You will have to regenerate your SDK and recompile your Silverlight applications, otherwise you will encounter a data abort exception in xamlruntime.dll:

Exception 'Data Abort' (0x4): Thread-Id=05d5006e(pth=c0891b0c), Proc-Id=04680072(pprc=c089196c) 'vplayer.exe', VM-active=04680072(pprc=c089196c) 'vplayer.exe'
PC=4017c64c(xamlruntime.dll+0x0002c64c) RA=40015b54(ceperf.dll+0x00005b54) SP=0007f91c, BVA=00000000

The bigger problem is that all of the Silverlight applications shipped with WEC7 (video player, music player, photo viewer, etc) are shipped as pre-compiled executables and are not build on the spot. You will have to do a manual build of public\mediapps\oak\samples\cemp followed by a clean sysgen of your OS Design to get the photo viewer, video and music player to work again.

Of course these executables should have been recompiled and included in update 35. Note that I only identified the media player applications, but there are of course many more control panel items and executables that need to be updated. I hope a next update will update all of the Silverlight dependent pre-compiled files in the WINCE tree!

WARNING: You may be tempted to do a "Build and Sysgen" (blddemo without -q) to build the entire tree. Don't ever do this! It will leave your WINCE tree in a undetermined state. Read this to understand why. In fact you should remove the dangerous build commands from the Advanced Build menu. Read this to learn how.

Follow these steps:

  1. Uninstall the SDK for your OS Design
  2. Update your WEC7 tree with the latest updates
  3. Open your OS Design
  4. In the Solution Explorer, browse to \public\mediaapps\oak\samples\cemp
  5. Right click cemp and choose "Rebuild (build -c)" from the context menu (do this for both a debug and retail build config!)
  6. Now select the root of your OS Design in the Solution Explorer
  7. And click "Clean Sysgen (blddemo clean -q)" from the Build->Advanced Build Commands menu (or Custom Build Commands menu if you followed these instructions)
  8. Build the SDK for the OS Design (make sure you built the dependent build configuration first)
  9. Close all Visual Studio instances
  10. Install your new SDK
  11. Recompile all of your Silverlight applications that were built against the previous SDK

Repeat step 5 for any other Silverlight components that do not work anymore after applying Update 35 (May 2014).

Standards vs Proprietary Modules

At GuruCE we work with a variety of boards from various customers. Some customers create their own boards from scratch, some use proprietary modules and yet some others choose a standard like Q7. We have found that standards, like Q7, work well for standard and well defined processor architectures like x86, but not so much for processor architectures that support a wide variety of functions even between different CPUs of the same family (like the Freescale iMX CPU range).

Martin Welford, CEO of our hardware partner Device Solutions has written a nice blog post about this and writes on the GuruCE blog as a guest writer:


Choosing a SOM – Why Standards don’t work for ARM modules

When choosing a System on Module (SoM) for a new project, you quickly find that there are some which follow a standard, and others which don’t. On the standards side, you may have heard of Q7 or SMARC. Proprietary modules use SO-DIMM connectors, board-to-board connectors or modules like our Topaz and Quartz products have no connector at all and solder directly to a board.

This post explores some of the reasons why we at Device Solutions have gone the proprietary non-standard path, and why we think standards don’t work for ARM and don’t deliver on the benefits you expect from a standard.

Why choose a standard?

For processor modules, having a second or third supply option sounds attractive. Obsolescence is a big problem and using a standard module to avoid this sounds like a compelling reason to go down that road. In fact it does work very well – for x86 based modules. However x86 chips are very well defined compared to the huge variation in features found on ARM based parts.

Why standards don’t work for ARM

ARM devices are used for a huge variety of applications, with an equally diverse set of requirements. If you have ever taken a close look at a Freescale i.MX part, you will see a lot of different functions and no single application makes use of them all.

This creates some big problems when trying to map an ARM chip to a standard interface. The challenges are:

  • Wide range of features, and not enough pins to expose them all.
  • Multiplexed features – which feature do you pick for a particular pin?
  • Functions change between chip generations and there may be no slot on the standard interface for a new feature. A good example of this is display interfaces – these have evolved from parallel TFT and chips have this interfaces along with LVDS, VGA or HDMI and now MIPI.

Designing with a standard ARM module

All this is not to say that ARM modules based on a standard don’t work at all. They do, but they might not do everything you expect (or need) them to do.
The first thing to check is, can you access all the features on the chip that you need? Make sure anything you think you might need is available. It is painful to get half-way through a design only to find you can’t get at a vital signal!

Using a standard module for second source reasons

This is where things get tricky and your design effort may effectively double. Because of all the reasons above, you can’t rely on two different standard ARM modules to give you exactly the same signals on the interface. It is important to identify what module you are going to use as a second source at the design stage. Every signal will need to be analysed and your base-board may need to have different build options for each module to get it working. Often this proves to be impossible because some vendors don’t share module schematics.

An alternative to second souring

Often the reason for having a second source is continuity of supply. Rather than invest in extra engineering (which is expensive and time consuming enough!), we suggest you ask the module vendor if they have an escrow service for their design files. This way should there be a problem for whatever reason, you will still have access to the module.

Conclusions

If you have made it this far, you will see that there are some issues with the current ARM based modules that follow a standard. Our advice is to choose a module that is the best fit for your design, and to solve second sourcing issues by other means.
There are a lot more details we haven’t gone into around this issue.

Contact us if you would like to know more.

Martin Welford
Device Solutions

13th MVP Award!

I've been honoured with the Microsoft MVP Award again this year!

Congratulations! We are pleased to present you with the 2014 Microsoft® MVP Award! This award is given to exceptional technical community leaders who actively share their high quality, real world expertise with others. We appreciate your outstanding contributions in Windows Embedded technical communities during the past year.

If my memory serves me right, this is now the 13th award in a row! Very happy to be recognized in the field again!

Both Michel Verhagen and Erwin Zwart of GuruCE are Microsoft Embedded Most Valuable Professionals; a recognition of their expertise and community involvement in the field of Windows Embedded Compact.

Windows Embedded Compact 2013 update out now!

The long awaited update for Windows Embedded Compact 2013 is now available for general download! The update now integrates Platform Builder and Application Builder with Visual Studio 2013. It also contains a (source) WiFi driver for the TI SDP44xx chipset, and a (source) BSP for the Freescale i.MX6 processor range.

More info here.

MSDN subscribers can download the new update iso through their subscription download center and the general public can obtain the update by registering and downloading here.

First look at Windows Embedded Compact 2013 running on Opal i.MX53

We are still finalizing the last bits of our Windows Embedded Compact 2013 (WEC2013) BSP, but here is a first look of the port on the Opal i.MX53 development kit:

GuruCE expert honoured with MVP award

MVP LogoGuruCE is proud to announce that Erwin Zwart, of our European branch, has been recognized by Microsoft for his technical excellence, community involvement and technical knowledge, and has received the Microsoft Most Valuable Professional (MVP) Award.

Congratulations Erwin!

Both Michel Verhagen (APAC branch) and Erwin Zwart (EMEA branch) are now MVPs, showcasing GuruCE's expertise in all things related to Microsoft Embedded products.

Opal r289

We've just released Opal: r289.

We believe our BSP for the Device Solution Opal iMX53 Module and Development kit is the best WEC7 BSP available for the Freescale iMX53 CPU. We have fixed many bugs in Freescale's stock code and added many new features, like for instance WIFI, full featured ECSPI drivers, GPIO drivers, etc.

The complete changelist can be found in the release notes.

Get Opal Release r289 now!

Note that the latest release can always be found at this link: http://guruce.com/opal/release/latest

No more SYNCHRONIZE_DRAIN!

Most BSPs use the SYNCHRONIZE_DRAIN and SYNCHRONIZE_BLOCK macros to serialize the build process on multiprocessor machines. Since almost all machines are multi-processor (or hyperthreading) nowadays, these macros really have to be used.

SYNCHRONIZE_DRAIN and SYNCHRONIZE_BLOCK work on the entire build tree, so if module "A" has the SYNCHRONIZE_DRAIN macro set in its sources file this module won't be build until all other modules in the entire build tree have been built, even if module "A" was only depending on one other module.

You can see that the brute force method of SYNCHRONIZE_DRAIN and SYNCHRONIZE_BLOCK can really slow down your build performance.

Luckily, there are some better macros we can use that provide a much more granular method of serializing the build system: BuildPassName_PRODUCES and BuildPassName_CONSUMES. These macros are now finally well documented in MSDN for WEC2013.

The usage of these macros is very easy and better to understand than the SYNCHRONIZE_DRAIN and _BLOCK macros:

The directory with a Sources file that contains a BuildPassName_PRODUCES macro with a particular value will be built before any directories with a corresponding BuildPassName_CONSUMES macro value. This build order happens regardless of the directories' position in the Dirs File.

In other words:

If module "A" needs module "B" to link, then module "B" has to add LINK_PRODUCES=B to its sources file and module "A" has to add LINK_CONSUMES=B to its sources file. This way module A won't be linked to module B before module B is built but any other non-dependent modules will continue to build in parallel. Easy as that!

You can specify multiple modules by separating them with a space:

LINK_CONSUMES=A B C D

Make sure to use unique names throughout your BSP tree, or build errors will result.

Freescale i.MX53 ECSPI silicon bug

We recently did a full rewrite of the Freescale ECSPI driver for our Opal i.MX53 BSP (now fully supporting all ECSPI features; master/slave mode, DMA, etc) and during testing we discovered a rather big problem with the ECSPI module inside the i.MX53...

For future reference and to prevent others from having to go through the same time-consuming research and analysis we decided to publish our findings through this blog post.

Problem Analysis

The ECSPI module shows unexpected behaviour when the bitcount is set to [(32 * n) + 1], where [n > 0].

As an example, let's walk through sending 1 x 33 bits.

In this case, we have to load the TXIFO with 2 UINT32s. The first UINT32 value contains 1 LSB and the 2nd UINT32 contains the remaining 32 bits.

For this transaction you'd have to configure the ECSPI registers as follows (33 bits, master mode, SS1, DRCTL don't care, loopback enable, no DMA, enable TC interrupt):

CONREG BURSTLENGTH      = 32
CONREG CHANNELSELECT    = 1
CONREG DRCTL            = 0
CONREG PREDIVIDER       = 0
CONREG POSTDIVIDER      = 0
CONREG CHANNELMODE      = 2
CONREG SMC              = 0
CONREG XCH              = 0
CONREG HT               = 0
CONREG EN               = 1

CONFIGREG HTLENGTH      = 0
CONFIGREG SCLKCTL       = 0
CONFIGREG DATACTL       = 0
CONFIGREG SSPOL         = 2
CONFIGREG SSCTL         = 2
CONFIGREG SCLKPOL       = 2
CONFIGREG SCLKPHA       = 0

INTREG TCEN             = 1
INTREG ROEN             = 0
INTREG RFEN             = 0
INTREG RDREN            = 0
INTREG RREN             = 0
INTREG TFEN             = 0
INTREG TDREN            = 0
INTREG TEEN             = 0

DMAREG RXTDEN           = 0
DMAREG RXDMALENGTH      = 0
DMAREG RXDEN            = 0
DMAREG RXTHRESHOLD      = 0
DMAREG TEDEN            = 0
DMAREG TXTHRESHOLD      = 0

STATREG TC              = 0
STATREG RO              = 0
STATREG RF              = 0
STATREG RDR             = 0
STATREG RR              = 0
STATREG TF              = 0
STATREG TDR             = 1
STATREG TE              = 1

PERIODREG CSDCTL        = 0
PERIODREG CSRC          = 0
PERIODREG SAMPLEPERIOD  = 0

TESTREG LBC             = 1
TESTREG RXCNT           = 0
TESTREG TXCNT           = 0

Load the TXFIFO with 2 UINTs as follows:

[0] 0x00000001
[1] 0xAAAAAAAA

The STAT & TEST registers reflect this:

STATREG TC              = 0
STATREG RO              = 0
STATREG RF              = 0
STATREG RDR             = 0
STATREG RR              = 0
STATREG TF              = 0
STATREG TDR             = 0
STATREG TE              = 0

TESTREG LBC             = 1
TESTREG RXCNT           = 0
TESTREG TXCNT           = 2

At this moment start the transfer by setting the XCH bit in the CONREG. This is when strange things start to happen:

CONREG BURSTLENGTH      = 32
CONREG CHANNELSELECT    = 1
CONREG DRCTL            = 0
CONREG PREDIVIDER       = 0
CONREG POSTDIVIDER      = 0
CONREG CHANNELMODE      = 2
CONREG SMC              = 0
CONREG XCH              = 0
CONREG HT               = 0
CONREG EN               = 1

CONFIGREG HTLENGTH      = 0
CONFIGREG SCLKCTL       = 0
CONFIGREG DATACTL       = 0
CONFIGREG SSPOL         = 2
CONFIGREG SSCTL         = 2
CONFIGREG SCLKPOL       = 2
CONFIGREG SCLKPHA       = 0

INTREG TCEN             = 1
INTREG ROEN             = 0
INTREG RFEN             = 0
INTREG RDREN            = 0
INTREG RREN             = 0
INTREG TFEN             = 0
INTREG TDREN            = 0
INTREG TEEN             = 0

DMAREG RXTDEN           = 0
DMAREG RXDMALENGTH      = 0
DMAREG RXDEN            = 0
DMAREG RXTHRESHOLD      = 0
DMAREG TEDEN            = 0
DMAREG TXTHRESHOLD      = 0

STATREG TC              = 1
STATREG RO              = 0
STATREG RF              = 0
STATREG RDR             = 1
STATREG RR              = 1
STATREG TF              = 0
STATREG TDR             = 1
STATREG TE              = 1

PERIODREG CSDCTL        = 0
PERIODREG CSRC          = 0
PERIODREG SAMPLEPERIOD  = 0

TESTREG LBC             = 1
TESTREG RXCNT           = 4
TESTREG TXCNT           = 0

As you can see from the above, the XCH bit is clear (good!), the TC interrupt fired (good!), but the RXFIFO contains not 2, but 4 UINT32s!

At this moment RXFIFO contains:

[0] 0x00000001
[1] 0x00000001
[2] 0x00000001
[3] 0xAAAAAAAA

As you can see, the first UINT32 from the TXFIFO is duplicated twice at the beginning of the RXFIFO.

The situation is slightly different with the 65 bit case. In that case the TC interrupt never fires:

We load the TXFIFO with 65 bits as follows:

[0] 0x00000001
[1] 0xAAAAAAAA
[2] 0x55555555
CONREG BURSTLENGTH      = 64

TESTREG LBC             = 1
TESTREG RXCNT           = 0
TESTREG TXCNT           = 3

All other registers are the same as per above.

Now we start the transfer by setting the XCH bit in the CONREG. This is when more strange things start to happen:

CONREG BURSTLENGTH      = 64
CONREG CHANNELSELECT    = 1
CONREG DRCTL            = 0
CONREG PREDIVIDER       = 0
CONREG POSTDIVIDER      = 0
CONREG CHANNELMODE      = 2
CONREG SMC              = 0
CONREG XCH              = 1
CONREG HT               = 0
CONREG EN               = 1

CONFIGREG HTLENGTH      = 0
CONFIGREG SCLKCTL       = 0
CONFIGREG DATACTL       = 0
CONFIGREG SSPOL         = 2
CONFIGREG SSCTL         = 2
CONFIGREG SCLKPOL       = 2
CONFIGREG SCLKPHA       = 0

INTREG TCEN             = 1
INTREG ROEN             = 0
INTREG RFEN             = 0
INTREG RDREN            = 0
INTREG RREN             = 0
INTREG TFEN             = 0
INTREG TDREN            = 0
INTREG TEEN             = 0

DMAREG RXTDEN           = 0
DMAREG RXDMALENGTH      = 0
DMAREG RXDEN            = 0
DMAREG RXTHRESHOLD      = 0
DMAREG TEDEN            = 0
DMAREG TXTHRESHOLD      = 0

STATREG TC              = 0
STATREG RO              = 0
STATREG RF              = 0
STATREG RDR             = 1
STATREG RR              = 1
STATREG TF              = 0
STATREG TDR             = 1
STATREG TE              = 1

PERIODREG CSDCTL        = 0
PERIODREG CSRC          = 0
PERIODREG SAMPLEPERIOD  = 0

TESTREG LBC             = 1
TESTREG RXCNT           = 5
TESTREG TXCNT           = 0

As you can see from the above, XCH stays set, the TC interrupt never fires and the TESTREG shows there are 5 UINT32s received in the RXFIFO (note that loopback is enabled!).

The contents of the RXFIFO are:

[0] 0x00000001
[1] 0x00000001
[2] 0xAAAAAAAA
[3] 0x00000001
[4] 0x55555555

As you can see, the 1st UINT32 from the TXFIFO is duplicated in the RXFIFO at position 0, 1 and 3.

The behavior is slightly different when doing the same but with SSCTL set to 0, but it's still not doing the right thing.

When doing a transfer of 33 bits with SSCTL set to 0, only 32 bits are sent:

Load TXFIFO with:

[0] 0x00000001
[1] 0xAAAAAAAA

Set XCH, wait for TC, result:

TXFIFO still contains:

[0] 0xAAAAAAAA

RXFIFO contains:

[0] 0x00000001
[1] 0x00000001

Note that SSCTL=0 works correct for bitcounts that are not [(32*n)+1] where [n>0].

Here are the scope captures of 32, 33, 34 and 35 bits, showing the i.MX53's bad behavior when transferring 33 bits:


yellow = chip select
blue = clock
purple = data

Freescale's response

We of course asked Freescale to verify this bug and they have. This is their response:

We are able to reproduce it. Unfortunately, there's no workaround for this error.

Other i.MX CPUs affected by this silicon bug?

We have verified the i.MX25 is NOT affected by this bug (but the i.MX25 contains an older version of the SPI block).
We have not been able to try our tests on the i.MX6 yet, but since the i.MX6 contains the exact same ECSPI block as the i.MX53, we suspect this issue will also be present on the i.MX6 series CPUs.

We have asked Freescale to update the errata sheets for the affected processor(s), but so far the erratas are silent about this issue.

More ECSPI silicon bugs?

Unfortunately; yes. Also the RX DMA TAIL mechanism (allowing non 32-word aligned DMA transfers to complete) does not work correctly. Here is the official response from Freescale:

We have confirmed this with the SDMA script owner. The script does not support the 32bytes unaligned DMA transfer.
This is a documentation bug. This will be corrected on documentation on next release.

Interesting way to solve silicon bugs... ;)

Conclusion

The ECSPI module inside the iMX53 CAN NOT BE USED for SPI transfers on 32 bit edges + 1.

When BURSTLENGTH is set to [(32 * n) + 1], the following happens:

For n = 0 (bitcount 1): All works fine!
For n = 1 (bitcount 33): TC interrupt fires, XCH bit clears, but more data than expected in RXFIFO (first UINT32 from TXFIFO duplicated).
For n > 1 (bitcount 65, 97, 129, etc): TC interrupt DOES NOT fire, XCH bit stays set, and also more data than expected in RXFIFO (first UINT32 from TXFIFO duplicated).

Windows Embedded Compact 7 and source control

A question that keeps coming back on the forums and from our customers that just start out with Windows Embedded Compact is; what files need to be put in source control, and which don't?

To answer this recurring question once and for all; here's a breakdown of all the files and folders that need to be put under version control, specific for Windows EC 7.

When putting files under source control you:

  1. Don't want to miss any file that is required for a successful build
  2. Don't want to add any file that is automatically generated by the build system
  3. Don't want to add any file that changes automatically every time you build

The last two would cause a new revision every time you build the kernel, which is normally not what you want; a new revision should only indicate changes YOU made.

With those rules in mind, here are the steps:

Windows Embedded Compact 7

First, clean up your BSP folder:

  1. Delete all obj folders from your BSP (eg C:\WINCE700\PLATFORM\Opal)
  2. The easiest way to do this is to search for obj using the search option of the standard Windows File Explorer. Then sort the search results so all obj folders are at the top, select them all (only select the obj folders, do not select any files!) and press the delete key. Once all obj folders are deleted another search for obj should not return anything.

  3. Delete all build.* files from your BSP
  4. The easiest way to do this is to search for build.* using the search option of the standard Windows File Explorer. Select all build*.dat, build.log, build.err and build.wrn files and press the delete key. Once all build.* files are deleted another search for build.* should not return anything.

  5. Delete all bldsys.* files from your BSP
  6. The easiest way to do this is to search for bldsys.* using the search option of the standard Windows File Explorer. Select all bldsys.log, bldsys.out, bldsys.err and bldsys.wrn files and press the delete key. Once all bldsys.* files are deleted another search for bldsys.* should not return anything.

You can now add the entire BSP folder to source control.

The next thing to add is any SOC folders your BSP may need to build correctly. In case of the Opal, we use SOC folders COMMON_FSL_V3 and MX53_FSL_V3 located in \WINCE700\PLATFORM\COMMON\SRC\SOC.

First, clean up these folders:

  1. Delete all obj folders from any SOC folder your BSP uses (eg C:\WINCE700\PLATFORM\COMMON\SRC\SOC\COMMON_FSL_V3)
  2. The easiest way to do this is to search for obj using the search option of the standard Windows File Explorer. Then sort the search results so all obj folders are at the top, select them all (only select the obj folders, do not select any files!) and press the delete key. Once all obj folders are deleted another search for obj should not return anything.

  3. Delete all build.* files from any SOC folder your BSP uses
  4. The easiest way to do this is to search for build.* using the search option of the standard Windows File Explorer. Select all build.dat, build.log, build.err & build.wrn files and press the delete key. Once all build.* files are deleted another search for build.* should not return anything.

  5. Delete all bldsys.* files from any SOC folder your BSP uses
  6. The easiest way to do this is to search for bldsys.* using the search option of the standard Windows File Explorer. Select all bldsys.log, bldsys.out, bldsys.err and bldsys.wrn files and press the delete key. Once all bldsys.* files are deleted another search for bldsys.* should not return anything.

You can now add the SOC folders your BSP uses to source control.

Finally, you need to add your OS Design(s).

Again, first we clean up:

  1. Remove the .ncb and .suo files from your OS Design root folder (eg C:\WINCE700\OSDesigns\Opal)
  2. These files can be found in the root folder of your OSDesign (eg C:\WINCE700\OSDesigns\Opal\Opal.ncb). The .ncb and .suo files are generated by Visual Studio every time you open the OS Design.

  3. Remove the .pbxml.user file from your OS Design subfolder (eg C:\WINCE700\OSDesigns\Opal\Opal)
  4. This is a file (eg C:\WINCE700\OSDesigns\Opal\Opal\Opal.pbxml.user) generated by Visual Studio every time you open the solution.

  5. Remove the RelDir folder (eg C:\WINCE700\OSDesigns\Opal\RelDir)
  6. This is the folder that will hold all files generated when your build your OS Design build configurations. The entire folder and its subfolders needs to be deleted.

  7. Remove the folder Wince700 from your OS Design subfolder (eg C:\WINCE700\OSDesigns\Opal\Opal\Wince700
  8. This folder contains the sysgenned files that will be generated every time you sysgen your OS Design. The entire folder and its subfolders needs to be deleted.

  9. Delete all obj folders from your OS Design subfolder (eg C:\WINCE700\OSDesigns\Opal\Opal)
  10. The easiest way to do this is to search for obj using the search option of the standard Windows File Explorer. Then sort the search results so all obj folders are at the top, select them all (only select the obj folders, do not select any files!) and press the delete key. Once all obj folders are deleted another search for obj should not return anything.

  11. Delete all build.* files from your OS Design subfolder (eg C:\WINCE700\OSDesigns\Opal\Opal)
  12. The easiest way to do this is to search for build.* using the search option of the standard Windows File Explorer. Select all build.dat, build.log, build.err & build.wrn files and press the delete key. Once all build.* files are deleted another search for build.* should not return anything.

You can now add your OS Design to source control.

This concludes the steps required to successfully put all files required for a successful Windows Embedded Compact 7 build under source control.

To build a clean version out of source control perform the following steps:

  1. Make sure you have committed all your changes
  2. Remove your BSP folder
  3. Remove any SOC folder your BSP uses
  4. Remove the OS Design
  5. Now checkout all the files from source control into your WINC700 tree
  6. Open the OS Design solution and immediately close Visual Studio again.
  7. This action creates the RelDir folder and allows a command line build.

  8. Right click the OSDesign pbxml (one folder higher), hover over Open Build Window and click on the build configuration you want to build
  9. In the DOS box, type blddemo -q
  10. NEVER EVER forget -q (as that would be the demonic Build and Sysgen command)!!!
    You may want to set the DOS box windows properties to a bigger window and bigger buffer. I usually set the Layout to 150/9999 for buffer size and 150/50 for window size. I also enable QuickEdit mode (on the Options tab).

Windows CE 6.0 and source control

A question that keeps coming back on the forums and from our customers that just start out with Windows CE is; what files need to be put in source control, and which don't?

To answer this recurring question once and for all; here's a breakdown of all the files and folders that need to be put under version control, specific for Windows CE 6.0 (including R2/R3).

When putting files under source control you:

  1. Don't want to miss any file that is required for a successful build
  2. Don't want to add any file that is automatically generated by the build system
  3. Don't want to add any file that changes automatically every time you build

The last two would cause a new revision every time you build the kernel, which is normally not what you want; a new revision should only indicate changes YOU made.

With those rules in mind, here are the steps:

Windows CE 6.0

First, clean up your BSP folder:

  1. Delete all obj folders from your BSP (eg C:\WINCE600\PLATFORM\Topaz)
  2. The easiest way to do this is to search for obj using the search option of the standard Windows File Explorer. Then sort the search results so all obj folders are at the top, select them all (only select the obj folders, do not select any files!) and press the delete key. Once all obj folders are deleted another search for obj should not return anything.

  3. Delete all build.* files from your BSP
  4. The easiest way to do this is to search for build.* using the search option of the standard Windows File Explorer. Select all build.dat, build.log, build.err & build.wrn files and press the delete key. Once all build.* files are deleted another search for build.* should not return anything.

  5. Remove *.bif your BSP folder
  6. .bif files are only generated in the root of your BSP (eg C:\WINCE600\PLATFORM\Topaz), so just select the BSP folder in the standard Windows File Explorer and delete any .bif files from there.

  7. Remove folders lib and target from your BSP
  8. These folders contain the libraries (lib) and executables (exe and dll) that result from building your BSP and are generated in the root of your BSP folder (eg C:\WINCE600\PLATFORM\Topaz).

  9. Finally remove the folder CESYSGEN\files from your BSP
  10. This folder contains the sysgenned files that will be generated every time you sysgen your BSP. Do not delete the CESYSGEN folder itself, just the files subfolder of the CESYSGEN folder needs to be deleted.

You can now add the entire BSP folder to source control.

The next thing to add is any SOC folders your BSP may need to build correctly. In case of the Topaz, we use SOC folders COMMON_TOPAZ_V1 and MX25_TOPAZ_V1 located in \WINCE600\PLATFORM\COMMON\SRC\SOC.

First, clean up these folders:

  1. Delete all obj folders from any SOC folder your BSP uses (eg C:\WINCE600\PLATFORM\COMMON\SRC\SOC\COMMON_TOPAZ_V1)
  2. The easiest way to do this is to search for obj using the search option of the standard Windows File Explorer. Then sort the search results so all obj folders are at the top, select them all (only select the obj folders, do not select any files!) and press the delete key. Once all obj folders are deleted another search for obj should not return anything.

  3. Delete all build.* files from any SOC folder your BSP uses
  4. The easiest way to do this is to search for build.* using the search option of the standard Windows File Explorer. Select all build.dat, build.log, build.err & build.wrn files and press the delete key. Once all build.* files are deleted another search for build.* should not return anything.

You can now add the SOC folders your BSP uses to source control.

Finally, you need to add your OS Design(s).

Again, first we clean up:

  1. Remove the .ncb and .suo files from your OS Design root folder (eg C:\WINCE600\OSDesigns\Topaz)
  2. These files can be found in the root folder of your OSDesign (eg C:\WINCE600\OSDesigns\Topaz\Topaz.ncb). The .ncb and .suo files are generated by Visual Studio every time you open the OS Design.

  3. Remove the .pbxml.user file from your OS Design subfolder (eg C:\WINCE600\OSDesigns\Topaz\Topaz)
  4. This is a file (eg C:\WINCE600\OSDesigns\Topaz\Topaz\Topaz.pbxml.user) generated by Visual Studio every time you open the solution.

  5. Remove the RelDir folder (eg C:\WINCE600\OSDesigns\Topaz\RelDir)
  6. This is the folder that will hold all files generated when your build your OS Design build configurations. The entire folder and its subfolders needs to be deleted.

  7. Remove the folder Wince600\cesysgen from your OS Design subfolder (eg C:\WINCE600\OSDesigns\Topaz\Topaz\Wince600\cesysgen)
  8. This folder contains the sysgenned files that will be generated every time you sysgen your OS Design. Do not delete the Wince600 folder itself, just the cesysgen subfolder of the Wince600 folder needs to be deleted.

  9. Remove the folder Wince600\BSP_CPU\sdk from your OS Design subfolder (eg C:\WINCE600\OSDesigns\Topaz\Topaz\Wince600\Topaz_ARMV4I\sdk)
  10. This folder contains the files to be included in the SDK that will be generated every time you build your BSP. Do not delete the Wince600 folder itself, just the sdk subfolder of the Wince600 folder needs to be deleted.

  11. Remove any file in the Wince600 folder
  12. This should just leave one folder named OAK

  13. Remove the folder Wince600\Topaz_ARMV4I\OAK\target and Wince600\Topaz_ARMV4I\OAK\lib from your OS Design subfolder (eg C:\WINCE600\OSDesigns\Topaz\Topaz\Wince600\Topaz_ARMV4I\OAK\target)
  14. This folder contains the resulting files from building your subprojects (so if you don't see them you don't have subprojects that generate either a library or an executable. Do not delete the OAK folder itself, just the target and lib subfolders of the Wince600\OAK folder need to be deleted.

  15. Remove any file in the Wince600\OAK folder
  16. This should just leave two folders named files and MISC

  17. Remove nlscfg.inf in the files folder (eg C:\WINCE600\OSDesigns\Topaz\Topaz\Wince600\Topaz_ARMV4I\OAK\target)
  18. This file is regenerated every time you open your OS Design.

  19. Remove any folder other than INTLTRNS in the files folder (eg C:\WINCE600\OSDesigns\Topaz\Topaz\Wince600\Topaz_ARMV4I\OAK\target)
  20. INTLTRNS contains the localized string translations. Any other folder will be regenerated.

  21. Delete all obj folders from your OS Design subfolder (eg C:\WINCE600\OSDesigns\Topaz\Topaz)
  22. The easiest way to do this is to search for obj using the search option of the standard Windows File Explorer. Then sort the search results so all obj folders are at the top, select them all (only select the obj folders, do not select any files!) and press the delete key. Once all obj folders are deleted another search for obj should not return anything.

  23. Delete all build.* files from your OS Design subfolder (eg C:\WINCE600\OSDesigns\Topaz\Topaz)
  24. The easiest way to do this is to search for build.* using the search option of the standard Windows File Explorer. Select all build.dat, build.log, build.err & build.wrn files and press the delete key. Once all build.* files are deleted another search for build.* should not return anything.

  25. Finally, edit your .sln file in your OS Design root folder (eg C:\WINCE600\OSDesigns\Topaz\Topaz.sln)
  26. An annoying bug in the Platform Builder plugin for Visual Studio 2005 is that it generates new GUIDs for the build configurations every time you open the solution file. This would cause a new revision in source control every time you open the OS Design (so without changing any files that would justify a new revision). To work around this bug always keep your .sln file clean:

    • Open the .sln file in Notepad (I recommend Notepad++) and remove all lines in between "GlobalSection(ProjectConfigurationPlatforms) = postSolution"
    • Now save the .sln file

    Now every time you commit your changes to the source control system, double check the changes in the .sln file. Normally nothing else but the GUIDs in between the postSolution section will change, so you can then just revert the changes back to the "clean" file in source control.

You can now add your OS Design to source control.

This concludes the steps required to successfully put all files required for a successful Windows CE 6.0 build under source control.

To build a clean version out of source control perform the following steps:

  1. Make sure you have committed all your changes
  2. Remove your BSP folder
  3. Remove any SOC folder your BSP uses
  4. Remove the OS Design
  5. Now checkout all the files from source control into your WINC600 tree
  6. Open the OS Design solution and immediately close Visual Studio again.
  7. When asked to save the solution click No. This action creates the RelDir folder and allows a command line build. You don't save the solution file to prevent the changing GUIDs in the solution file.

  8. Right click the OSDesign pbxml (one folder higher), hover over Open Build Window and click on the build configuration you want to build
  9. In the DOS box, type blddemo -q
  10. NEVER EVER forget -q (as that would be the demonic Build and Sysgen command)!!!
    You may want to set the DOS box windows properties to a bigger window and bigger buffer. I usually set the Layout to 150/9999 for buffer size and 150/50 for window size. I also enable QuickEdit mode (on the Options tab).

Topaz Release 822

We've just released another Topaz BSP release: r822. This release fixes issues in FlexCAN, GPIO, bootloader, LCD, SD and SDK headers and it adds the General Purpose Timers (GPT) SDK to the kernel.

See the release notes for a complete change list.

Starting with this release we have retired the Topaz Binary BSP. The Topaz Binary BSP was not very useful since most customers sooner or later needed to change pin muxing and without source this is impossible. We now offer the full Topaz Source BSP for free with any of our support contracts.

Get Topaz Release r822 now!

Note that the latest release can always be found at this link: http://guruce.com/topaz/release/latest

Opal Release 160

We've just released the initial version of the Opal: r160.

We believe our BSP for the Device Solution Opal iMX53 Module and Development kit is the best WEC7 BSP available for the Freescale iMX53 CPU at this moment. We have fixed many bugs in Freescale's stock code and added many new features, like for instance fully working WIFI! As far as we know we are supporting the only WEC7 board with (working) integrated 802.11 a/b/g/n WIFI on the market today.

The complete changelist is simply too long to list in this blog post, but have a look in the release notes if you are interested.

Get Opal Release r160 now!

Note that the latest release can always be found at this link: http://guruce.com/opal/release/latest

Topaz Release 706

We've just released another Topaz BSP release: r706. This release contains a fix for the low power mode of FlexCAN and adds support for UART4 and UART5 in the bootloader (for redirecting Windows CE debug output).

Here's the complete changelist (see also the release notes):

CE 6.0 R3 Release 706

  • Shorter timeout for NETUI when using a headless kernel
  • Added bootloader support for serial output on UART4 and UART5
  • Fixed issue with FlexCAN low power mode D4

Get Topaz Release r706 now!

Note that the latest release can always be found at this link: http://guruce.com/topaz/release/latest

Full featured WEC7 OS Design builds in 5-10 minutes

With the exponential growth of Windows Embedded Compact 7 kernel images unfortunately also the build time grows exponentially. I have always used VMWare Workstation to keep my Windows CE build environments separate (as side-by-side installations are not supported). Unfortunately, with WEC7 it felt like being back in Windows CE 3.0 days... In those days it was not the size of the kernel causing clean sysgen's to take over 40 minutes to build but it was slow hardware. I've always been able to keep build times below 25 minutes using VMWare and fast hardware, but with WEC7 this proved impossible. On my Core I5 2.6 Ghz with 16 MB DDR3 memory and a 7200 rpm SATA3 disk a WEC7 build inside a VM running Win7 took over 50 minutes!

To speed up the builds I have now invested in a separate laptop, configured specifically to build WEC7 kernels. It's an ASUS N55SF with an Intel® Core™ i7 2670QM/2630QM Processor, 6M cache, 4 cores/8 threads, 3.1 GHz and 8 GB DDR3 memory.

As you may know, processor speed is only one part of the equation with the other big factor being disk speed. I therefore invested in one of the fastest SSD's available at the moment: the OCZ Vertex 4: SATA 3, 6 GB/s, 2.5" SSD with a max of 120k IOPS and 550 MB/s read and 475 MB/s write speed. I chose the 512 GB version as it is the fastest and with the huge kernels and installation size of WEC7 you simply need the space.

Even with this hardware running a VMWare Win7 image a build still took 25 minutes; quite an improvement from the >50 minute builds on my Core I5 with 7200 rpm SATA 3 disk but still too long. I then decided to ditch VMWare and try a native install with the OS on the SSD, together with VS2K5 and WEC7 Platform Builder.

After speeding up the build system using the method described in this blog post, my builds are now 9 minutes for a reasonably feature rich WEC7 kernel (and <5 minutes for small headless kernels). Finally we're back in the 21st century again! It sure took some time and money to speed up WEC7 builds but if you are building a lot of kernels the investment will pay itself off after a couple of weeks!

Here's the full specification (of all components relating to build speed):

  • Intel® Core™ i7-2670QM Processor (6M Cache, up to 3.10 GHz)
  • Room for improvement here if you go for a desktop system.

  • DDR3 1333 MHz SDRAM
  • Room for improvement here if you go for a desktop system.

  • 512 GB OCZ Vertex 4: SATA 3, 6 GB/s, 2.5" SSD
  • The Patriot Wildfire is the fastest according to passmark.com but the OCZ Vetex 4 is unbeatable in price/performance.

  • OS: Windows 7 64 bit

Unfortunately side-by-side installation of Windows CE 6.0 and WEC7 is not supported, but I'd love to see how fast a CE 6.0 kernel will build on this machine... Under a minute?

Conclusion:

When building kernels with WEC7 it's worth to fork out some cash to speed up the builds if you are building lots of kernels (or if you just don't want to drink so much coffee on a day ;)

PS: All build times are Debug OS Designs, with shell and built with blddemo clean -q

Topaz Release 676

We've just released a major upgrade to the Topaz BSP. The main feature we added in this release is support for a boot splash screen. Due to the changes in memory layout you will need to install the new Topaz Flasher and this new Topaz Flasher can not be used with Topaz releases older than r676.

Here's the changelist:

CE 6.0 R3 Release 676

  • Changed version number and product name of Topaz SDK
  • Updated FlexCAN driver source
  • Now turning backlight off after 10 minutes (instead of 1 minute)
  • Fixed serial debug redirection in bootloader (now works)
  • Added boot splash screen support from NAND and SD
  • Added registry setting so NETUI times out quicker on headless configurations

Get Topaz Release r676 now!

Note that the latest release can always be found at this link: http://guruce.com/topaz/release/latest

Topaz Release 589

We've just release a new version of the Topaz Binary BSP, Image Binaries and SDK containing some bug fixes and improvements to the BSP.

Get Topaz Release r589 now!

Note that the latest release can always be found at this link: http://guruce.com/topaz/release/latest

Adding Bluetooth support to a WEC7 kernel

Recently I had to add Bluetooth support to a custom board running Windows Embedded Compact 7. The Bluetooth module was CSR compatible connected to UART2. To enable the driver for this module you'll have to add the following registry settings to platform.reg:

; @CESYSGEN IF CE_MODULES_BTHCSR
[HKEY_LOCAL_MACHINE\Software\Microsoft\Bluetooth\Transports\BuiltIn\1]
    "driver"="bthcsr.dll"
    "flags"=dword:1
    "name"="COM2:"
    "baud"=dword:1c200
    "resetdelay"=dword:300
; @CESYSGEN ENDIF

You'll have to adjust the baudrate and COM port to match your configuration. The Bluetooth universal loader (SYSGEN_BTH) first looks for PnP devices (like USB/SDIO) and if it can't find any of those it will look for devices listed in the above Bluetooth\Transports\BuiltIn key in the order they are listed.

When I first added support for Bluetooth I just clicked all of the available items in the catalog:

  • Bluetooth Profile Management APIs (SYSGEN_BTH_BTHUTIL)
  • Bluetooth Stack with Universal Loadable Driver (SYSGEN_BTH)
  • Bluetooth HID - Keyboard (SYSGEN_BTH_HID_KEYBOARD)
  • Bluetooth HID - Mouse (SYSGEN_BTH_HID_MOUSE)
  • Bluetooth HS/HF and Audio Gateway Service (SYSGEN_BTH_AG)
  • Bluetooth PAN (SYSGEN_BTH_PAN)
  • Bluetooth Settings UI (SYSGEN_BTH_SETTINGS)

After rebuilding the OS Design the universal loader just did not want to load the driver I specified under the Bluetooth\Transports\BuiltIn key. Without (premium shared) source code of the universal loader it was very difficult to find out what was going wrong. All I could go on was the debug messages outputted by the Bluetooth Module, which I enabled by adding this key to the device's registry (in OSDesign.reg):

; Debug Zones
[HKEY_LOCAL_MACHINE\DebugZones]
        "BTD"=dword:E404

Unfortunately the debug messages did not give any clue as to why it did not want to load the driver I specified. In the end I decided to just select the Universal Loadable Driver (SYSGEN_BTH) in the catalog and deselect everything else, just to get rid of all other unrelated Bluetooth messages. To my surprise all of a sudden the driver did load!

I copied the reginit.ini from the release folder to a temporary folder, selected all Bluetooth components again and rebuilt the kernel. I then compared the reginit.ini of the kernel with only the universal loader component selected with the reginit of the kernel with all Bluetooth components selected. That way I discovered the culprit component was the Bluetooth Profile Management APIs (SYSGEN_BTH_BTHUTIL) component.

This component adds the following registry settings:

[HKEY_LOCAL_MACHINE\Software\Microsoft\Bluetooth\sys]
   "Power"=dword:0                  ; Radio off by default
   "ScanMode"=dword:0               ; Radio is not discoverable
   "DisableAutoSuspend"=dword:1     ; Allow suspend when connected

This disables the radio by default, hence the driver did not load.

So, when you want to enable Bluetooth at boot and you include the Bluetooth Profile Management APIs component, you'll have to override these registry values either inside platform.reg or OSDesign.reg:

; @CESYSGEN IF CE_MODULES_BTD
; @CESYSGEN IF OSSVCS_MODULES_BTHUTIL
[HKEY_LOCAL_MACHINE\Software\Microsoft\Bluetooth\sys]
   "Power"=dword:1                  ; Radio on by default
   "ScanMode"=dword:3               ; Radio is discoverable
   "DisableAutoSuspend"=dword:1     ; Allow suspend when connected
; @CESYSGEN ENDIF OSSVCS_MODULES_BTHUTIL
; @CESYSGEN ENDIF CE_MODULES_BTD

WEDU endless loop in registration pages problem: solved

When I tried WEDU (the Windows Embedded Developer Update) I was never able to get past the pages where you have to register. After some back and forth with Microsoft it seems you HAVE to select "Yes, I agree" to the question "Do you agree that a Windows Embedded representative may contact you using the phone number or email address provided by you to supply further Windows Embedded Information", even though the options are "Please select from the Following Options", "Yes, I agree" and "No, I do not agree":

See also this forum post.

Initially I selected "No, I do not agree" because I do not want any unsolicited calls from Microsoft or one of my competitors ;). When you select that option WEDU does not throw any warning message, nothing in red, it just simply loops back to the registration page until you select "Yes, I agree".

I think this can be classified as a bug...

So, if you also encounter the WEDU endless registration loop problem; select "Yes, I agree" and say "No, I do not agree" out loud when you select it. That'll do it! :P

Hosting webservices on WEC using gSOAP: Windows Phone 7.5 Mango Client Application

In the previous 2 articles (this and this one) I showed you how to create .NET C# based client applications consuming a webservice running on a Windows Embedded Compact device. In this blog post I will show you how to do the same, but then with a client application running on Windows Phone 7.5!

Convert RPC/encoded to RPC/literal

When you try to use the existing wsdl file in a silverlight solution for WP7 you won't be able to use the operations as described. It seems that the silverlight version doesn't support RPC/encoded soap and ignores those operations. It does support RPC/Literal soap style so we need to change the style from RPC/encoded to RPC/literal. This is actually really easy: Open the wsdl file and modify the input and output operations from

<soap:body use="encoded" ..../>

to

<soap:body use="literal" ..../>.

In XMLSpy:

You can see that the HelloWorld_Binding, the middle part, has changed. The input and output body styles have been modified from encoded to literal. That's basically all you need to change. This change in the wsdl file requires us to modify and rebuild the server and client code. Let's start with the server side.

Rebuild HelloWorldWebService solution

Open the existing "HelloWorldWebService.sln" solution we created in the previous blog post.

  1. Right-click the wsdl file and choose “compile”.
  2. Now recompile our (regenerated) header file: Right-click on the HelloWsdl.h file and choose "compile". This will re-generate a bunch of files as well.
  3. Finally; build the solution...

Oops! Unresolved externals... The reason for these is that we changed the message style and therefore we need to construct (or actually gSOAP needs to construct) the soap message slightly different.

In the HelloWorldWebservice solution open HelloWsdlMethods.cpp and modify the content to match the following:

// int ns1__HelloWorldOperation(struct soap*, char *name, char* &answer)
int ns1__HelloWorldOperation(struct soap*, char *name, struct ns1__HelloWorldOperationResponse &_param_1)
{
        printf("Hello my method\r\n");
        char* myName = {"Erwin"};
        //answer = myName;
        _param_1.answer = myName;
        return SOAP_OK;
}

//int ns1__UploadResource(struct soap*, char *resourceName, xsd__base64Binary resource, unsigned int resourceId, unsigned int &resourceId_)
int ns1__UploadResource(struct soap*, char *resourceName, xsd__base64Binary resource, unsigned int resourceId, struct ns1__UploadResourceResponse &_param_2)
{
        printf("Upload resource called (name: %s, id: %d", resourceName, resourceId);
        int size = resource.__size;
        return SOAP_OK;
}

I commented the original code and added new code to emphasize the code differences.

Windows Phone 7.5 (Mango) Client application

Now lets create a Windows Phone 7.5 client application. Make sure you have the Windows Phone SDK 7.1 installed. You need this to build applications for WP7/7.5.

Open Visual Studio 2010 Express for Windows Phone and create a new application (File | New Project). In the C# section "Silverlight for Windows Phone" choose "Windows Phone Application":

Choose TestWebServiceWP7 as the solution name and leave the other options as is. Select "OK" to create the solution.
I'll leave the sample as simple as possible just to illustrate how to consume a webservice running on a Windows Embedded Compact device. Drag 2 buttons on the content panel. Name the first button "Test" and the other "UploadImage". Something like:

Double-click the test button so we can enter the code behind. First thing we need to do is add our (Web)Service reference. In the solution explorer right click on the "Service Reference" and choose "Add Service Reference". Select HelloWsdl, created in the beginning of this article. You should be seeing something like:

As you can see I named the service "HelloWsdlService". Click "OK" to add the web service to our solution. Add the following code to complete the Test button implementation:

namespace TestWebServiceWP7
{
    public partial class MainPage : PhoneApplicationPage
    {
        private HelloWsdlService.HelloWorld_PortTypeClient service = null;

        // Constructor
        public MainPage()
        {
            InitializeComponent();

            // Create service
            service = new HelloWsdlService.HelloWorld_PortTypeClient();

            // Event handler for asyn operation hello world
            service.HelloWorldOperationCompleted += new EventHandler<HelloWsdlService.HelloWorldOperationCompletedEventArgs>(HelloWorldOperation_Completed);
        }

        private void Test_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                // Calling our webservice hello world
                service.HelloWorldOperationAsync("Erwin");
            }
            catch (Exception ex)
            {
                // Handle errors here
            }
        }

        void HelloWorldOperation_Completed(Object o, HelloWsdlService.HelloWorldOperationCompletedEventArgs args)
        {
            PageTitle.Text = args.Result.ToString();
        }
               
    }
}

As you can see I create the service class and the event handler which handles the HelloWorldOperation asynchroneously in the constructor. The button test code (Test_Click) calls HelloWorldOperation. Deploy your solution to either the WP7 emulator or to your Windows Phone device. Run the webservice code on the Topaz (as describer in the other articles) and run the application on the WP7 emulator or device.

To illustrate the complete solution check out this video showing me uploading and running the Windows Phone 7 application and the webservice on the Topaz. It is a recording of my desktop (I am using Cerdisp and Cerhost as an RDP solution to show the desktop of the Windows Embedded Compact device):

If you have any questions please leave a comment below!

Reconstructing WEC7 web updates for offline installation

[UPDATE: This issue has been resolved through the "Offline Layout" feature]

Microsoft changed the update mechanisms in the new version of Windows CE; Windows Embedded Compact 7. The update mechanism definitely needed an update, but in my humble opinion Microsoft made it worse instead of better...

We now have WEDU, the Windows Embedded Developer Update, a great idea if it would work. WEDU simply does not work inside a VMWare virtual machine and even if it works it always downloads the updates without a possibility to install from previously downloaded update packages.

That doesn't really matter if you have only 1 installation to keep updated, but what if you have multiple? Downloading gigabytes of data multiple times might not be time consuming or costing a lot of money in places where internet is fast and uncapped, but here in NZ internet is slow, expensive and capped so I really only want to download once and install often (see also this and this post).

If you have access to the Mobile & Embedded Communications Extranet (ECE) you can download most updates in img format, but unfortunately not all updates include all files needed and thus they are still web installers.

Luckily there are ways to reconstruct web installers so that you only have to download once and install (offline) often.

As an example we'll take the Windows Embedded Compact 7 Monthly Update July 2011:

  1. Download WindowsEmbeddedCompact7.exe
  2. Delete all subfolders under C:\Documents and Settings\All Users\Application Data\PkgCache
  3. Run the update
  4. When asked for a location for the files, click the box "Allow downloading for all files" and the click button "Download"
    NB: Everything from the OS, CTK and PB locations (see tree structure below) can be copied directly from the original WEC7 DVDs or you can point the installer to the DVD
  5. Once the download is finished you'll find the downloaded files in a GUID named subfolder of the PkgCache folder (full path above)
  6. Copy the downloaded files to a subfolder named "1" in the folder you placed WindowsEmbeddedCompact7.exe
  7. Continue the installation (NOTE: Do NOT exit the installation at any time before you have copied the files from the PkgCache folder! When you exit the update application ALL downloaded files will be deleted!)
  8. Repeat the steps above and increase the number of the subfolder you copy the files in for every component downloaded
  9. Once all components are downloaded and copied, exit the update application before it starts installing
  10. Restart the update application
  11. It will ask again for the missing files. Take note of the path it is showing in the box, for instance: [.]\OS\Common\Common.msi (for Architecture Common files)
  12. Rename the subfolder to match the expected location. So if you copied the common architecture update files into subfolder "4" you recreate the expected location (".\OS\Common") and move the files there
  13. Do this for all components and press OK
  14. The installer should then find the files and check if they are correct
  15. If all the files are in their correct places the installer does not show a download dialog anymore but instead will directly show a "Begin Install" window
  16. You can now run the installer for multiple (offline) installations

For reference, this is the entire directory tree required for offline installation of the July 2011 WEC7 update:

+---CTK (***)
+---OS (***)
|   +---Common
|   +---processor
|   |   +---ARMV5
|   |   +---ARMV6
|   |   +---ARMV7
|   |   +---MIPSII
|   |   +---MIPSII_FP
|   |   \---x86
|   \---sharedsource
+---PB (***)
+---Update1
|   \---Documentation
\---Update2
    \---OS
        +---processorconfig
        |   +---armv5
        |   |   +---checked
        |   |   +---debug
        |   |   \---retail
        |   +---armv6
        |   |   +---checked
        |   |   +---debug
        |   |   \---retail
        |   +---armv7
        |   |   +---checked
        |   |   +---debug
        |   |   \---retail
        |   +---MIPSII
        |   |   +---checked
        |   |   +---debug
        |   |   \---retail
        |   +---MIPSII_FP
        |   |   +---checked
        |   |   +---debug
        |   |   \---retail
        |   \---X86
        |       +---checked
        |       +---debug
        |       \---retail
        \---sharedsource

(***) This folder and all subfolders are copied from original WEC7 DVDs. You can also just point the installer to the DVD when asked for these files.

Hosting webservices on Windows Embedded Compact using gSOAP: Uploading a file

By popular demand here's a simple example how to upload a file using a gSOAP webservice. This is a follow up post; for this example you first need to read and implement the code from the first post in this series.

With the base code of the first post it is now very simple to extend that solution's functionality because we've already completed the most difficult part; setting up your solution.

In this follow up blog post I will create a method that allows you to upload a file to the Topaz target:

int UploadResource(String resourceName, byte[] resource, uint resourceId, uint &statusCode);

First we need to modify the existing wsdl file (our webservice design). I won't list the entire wsdl file here because it's getting a bit long. Instead I have downloaded a tool that allows me to graphically design my wsdl file; Altova XML spy. Altova XML spy is not a free tool but it is very nice to work with it. There may be some free tools that offer the same functionality as well (let me know if you find one). I opened the wsdl file we created in the previous blog post in this series and opened it in XML spy for modification. After modification it looks like this:

Note the port type "UploadResource" I added (left block under HelloWorld_PortType). I added the input parameters resourceName (String), resourceId (unsinged integer) and a byte buffer for the actual data (base64Binary). I also added an output parameter statusCode. In the code I am not doing anything with it, but I just put it there to illustrate an output parameter. To complete the wsdl design we need to add the encodingStyle and namespace fields for the binding (the middle part of the picture). Just copy'n'paste it from the HelloWorldOperation. For complete reference I've attached the wsdl file to this blog post. It's a good example how to construct your webservice interface. It also shows a wsdl file can get complicated very quickly so a design tool like the one I used is recommended.

Open the existing "HelloWorldWebService" solution we created in the previous blog post. You're now just a few simple steps away from finishing the server side code:

1). Right click the wsdl file and choose “Compile”. Now our header (HelloWsdl.h2). Right click on the HelloWsdl.h file and choose "compile". Again this will re-generate a bunch of files as well.

If you would now build your solution you will end up with some unresolved externals; our newly created method. You can find the declaration of this new method in the generated soapStub.h file:

/******************************************************************************\
 *                                                                            *
 * Server-Side Operations                                                     *
 *                                                                            *
\******************************************************************************/

SOAP_FMAC5 int SOAP_FMAC6 ns1__HelloWorldOperation(struct soap*, char *name, char *&answer);

SOAP_FMAC5 int SOAP_FMAC6 ns1__UploadResource(struct soap*, char *resourceName, xsd__base64Binary resource, unsigned int resourceId, unsigned int &statusCode);

Open HelloWsdlMethods.cpp and add the following method implementation:

int ns1__UploadResource(struct soap*, char *resourceName, xsd__base64Binary resource, unsigned int resourceId, unsigned int &statusCode)
{
    printf("\r\nUpload resource called\r\nname: %s, id: %d ", resourceName, resourceId);
    return SOAP_OK;
}

Don't let the xsd__base64Binary type scare you. It's just a class with some members including:
__ptr: A character pointer to the memory buffer (char* buf) containing the contents of the file.
_size: Contains the number of bytes in the buffer above.

4). Build your solution and run the server code on your Topaz!

The C# Managed Client

In the previous blog post in this series we've created a Testclient application written in C# to test our webservice. Open the "TestWebServiceClient" solution. Add an OpenFileDialog control and a button control to the Form. Call the button something like UploadFile:

Double click on the newly added button which will create the button click event code behind. Now implement the event handler with the following content:

// Opens a file dialog and uploads the file selected
private void UploadFile_Click(object sender, EventArgs e)
{
  if (openFileDialog1.ShowDialog() == DialogResult.OK)
  {
    byte[] data = GetBytesFromFile(openFileDialog1.FileName);
   
    if(data.Length > 0)
    {
       uint id = 22;
       try
       {
         server.UploadResource(openFileDialog1.FileName, data, ref id);
       }
       catch (Exception ex)
       {
         MessageBox.Show("Failed to upload resource" + ex.Message);
       }
    }
 }
}

// Reads a file and returns the content as a byte[]
public static byte[] GetBytesFromFile(string fullFilePath)
{
  // this method is limited to 2^32 byte files (4.2 GB)
  FileStream fs = File.OpenRead(fullFilePath);
  try
  {
    byte[] bytes = new byte[fs.Length];
    fs.Read(bytes, 0, Convert.ToInt32(fs.Length));
    fs.Close();
    return bytes;
  }
  finally
  {
    fs.Close();
  }
}

Of course this is just a sample and you should take of security and parameter checking. In this sample I have left this stuff out just to keep it as simple as possible.

When you run the HelloWorldWebservice on the target device and run the C# Client sample on your pc, a file open dialog will appear when you click on the "Upload File" button. Select a file (I just selected desktop.ini) and it will upload the contents of that file to the webservice on the target.

If you implemented everything correctly you should be seeing the following output on the target:

That's it!

To recap: To modify or extend your webservice, create/modify the wsdl. Compile the wsdl file, compile the header file. Implement the native webservice. When that's done, modify your client by refreshing the imported wsdl file and you can call your new webservice.

Happy 2012 to all our readers!

FlexCAN usage example

Since we included our iMX FlexCAN driver in the iMX25 Topaz image binaries we have received a couple of requests for sample code, so here it is:

#include "DebugZones.h"
#include "FlexCANSDK.h"

#define CAN_BITRATE         (125*1000)  // 125 kHz
#define CAN_READ_INTERVAL   1000        // Set to INFINITE to wait forever for a CAN Frame
#define CAN_WRITE_INTERVAL   500

CRITICAL_SECTION gCS;
LPCWSTR DataToHexString(const PCAN_FRAME pCANFrame)
{
    static WCHAR szHexString[100];
    WCHAR szHexByte[7];
    EnterCriticalSection(&gCS);
    {
        szHexString[0] = 0;
        if (pCANFrame->btDataLen)
        {
            wsprintf(szHexByte, L"0x%02X", pCANFrame->btData[0]);
            wcscat_s(szHexString, _countof(szHexString), szHexByte);
            for (int i=1; i<pCANFrame->btDataLen; i++)
            {
                wsprintf(szHexByte, L", 0x%02X", pCANFrame->btData[i]);
                wcscat_s(szHexString, _countof(szHexString), szHexByte);
            }
        }
    } LeaveCriticalSection(&gCS);
    return szHexString;
}

DWORD WINAPI CANReadThread(LPVOID lpParameter)
{
    HANDLE hFinishEvent = (HANDLE)lpParameter;
    CAN_FRAME canFrame = {0};
    canFrame.bExtended = CAN_EXTENDED;  // Receive any extended frame
    LOGMSG(ZONE_INFO, L"CAN Read Thread initialized; waiting for %s CAN frames...\r\n", (CAN_EXTENDED == canFrame.bExtended) ? L"Extended" : L"Standard");
    do
    {
        if (ERROR_SUCCESS == FlexCANReadFrame(1, &canFrame, CAN_READ_INTERVAL, 0))
        {   // Convert data to string, copy the string and show received frame
            LPWSTR pszData = wcsdup(DataToHexString(&canFrame));
            if (canFrame.bExtended == CAN_EXTENDED)
                LOGMSG(ZONE_INFO, L"Read CAN frame with ID 0x%08X, Data: %s\r\n", canFrame.ui32ID, pszData);
            else
                LOGMSG(ZONE_INFO, L"Read CAN frame with ID 0x%03X, Data: %s\r\n", canFrame.ui32ID, pszData);
            // Clean up
            free(pszData);
        }
    } while (WAIT_TIMEOUT == WaitForSingleObject(hFinishEvent, 0));

    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
    // Register with debug subsystem
    RETAILREGISTERZONES(NULL);

    InitializeCriticalSection(&gCS);

    HANDLE hFinishEvent = NULL;
    HANDLE hReadThread = NULL;
    for (;;)
    {   // Use loop for easy break-out error handling
        if (!FlexCANInit(1))
            break;

        DWORD dwBitrate = CAN_BITRATE;
        if (!FlexCANSetBitrate(1, &dwBitrate))
            break;
        LOGMSG(ZONE_INFO, L"Bitrate set to %d (requested %d)\r\n", dwBitrate, CAN_BITRATE);

        hFinishEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        if (!hFinishEvent)
            break;

        // Create the read thread that will receive all CAN Frames
        hReadThread = CreateThread(NULL, 0, CANReadThread, (LPVOID)hFinishEvent, 0, NULL);
        if (!hReadThread)
            break;

        // Some time to see CAN Frames coming in
        Sleep(10000);

        CAN_FRAME canFrame = {0};
        for (int i=0; i<10; i++)
        {   // Send a couple of frames at highest priority (0)
            canFrame.bExtended = CAN_EXTENDED;
            canFrame.btData[0] = 0x10;
            canFrame.btData[1] = 0x11;
            canFrame.btData[2] = 0x12;
            canFrame.btDataLen = 3;
            canFrame.ui32ID = 0x12345678;
            DWORD dwRet = FlexCANWriteFrame(1, CAN_DATA, CAN_TXNOW, 0, &canFrame, 500);
            LOGMSG(ZONE_INFO, L"Frame with ID 0x%08X and data %s sent. Result: %d\r\n", canFrame.ui32ID, DataToHexString(&canFrame), dwRet);
            Sleep(CAN_WRITE_INTERVAL);
        }

        // Signal read thread to finish
        SetEvent(hFinishEvent);
        if (WAIT_OBJECT_0 != WaitForSingleObject(hReadThread, CAN_READ_INTERVAL*2))
            TerminateThread(hReadThread, 0);

        // And wait for a frame with ID 0x12345678 for 10 seconds (no other CAN frames are accepted during this time)
        canFrame.bExtended = CAN_EXTENDED;
        canFrame.ui32ID = 0x12345678;
        LOGMSG(ZONE_INFO, L"Waiting for extended frame with ID 0x%08X...\r\n", canFrame.ui32ID);
        DWORD dwRet = FlexCANReadFrame(1, &canFrame, 10000, 0x1FFFFFFF);
        LOGMSG(ZONE_INFO, L"Specific FlexCANReadFrame result: %d\r\n", dwRet);
        if (ERROR_SUCCESS == dwRet)
            LOGMSG(ZONE_INFO, L"Read CAN frame with ID 0x%08X, Data: %s\r\n", canFrame.ui32ID, DataToHexString(&canFrame));

        // Now read any frame (ui32IDMask set to 0)
        LOGMSG(ZONE_INFO, L"Waiting for extended frame with any ID...\r\n");
        dwRet = FlexCANReadFrame(1, &canFrame, 10000, 0);
        LOGMSG(ZONE_INFO, L"Unspecific FlexCANReadFrame result: %d\r\n", dwRet);
        if (ERROR_SUCCESS == dwRet)
        {   // Show received frame
            if (canFrame.bExtended == CAN_EXTENDED)
                LOGMSG(ZONE_INFO, L"Read CAN frame with ID 0x%08X, Data: %s\r\n", canFrame.ui32ID, DataToHexString(&canFrame));
            else
                LOGMSG(ZONE_INFO, L"Read CAN frame with ID 0x%03X, Data: %s\r\n", canFrame.ui32ID, DataToHexString(&canFrame));
        }

        // Now transfer a whole bunch of messages in one go
        CAN_TRANSFER_BLOCK canBlock;
        canBlock.dwNumPackets = 32;
        canBlock.pCANPackets = new CAN_PACKET[canBlock.dwNumPackets];
        if (canBlock.pCANPackets)
        {
            for (DWORD i=0; i<canBlock.dwNumPackets; i++)
            {   // Init packets; data from max to 0
                canBlock.pCANPackets[i].bRemote = CAN_DATA;             // Data frame
                canBlock.pCANPackets[i].btIndex = 0xFF;                 // Let driver find a free message buffer
                canBlock.pCANPackets[i].btPriority = 0;                 // Highest priority
                canBlock.pCANPackets[i].bTxOnRemoteRx = CAN_TXNOW;      // Transfer immediately
                canBlock.pCANPackets[i].bWrite = CAN_WRITE;             // We want to write this frame
                canBlock.pCANPackets[i].canFrame.bExtended = CAN_STANDARD;  // Standard address (11 bit)
                canBlock.pCANPackets[i].canFrame.btData[0] = (BYTE)(0xFF - (i*(256/canBlock.dwNumPackets)));
                canBlock.pCANPackets[i].canFrame.btDataLen = 1;         // 1 byte data
                canBlock.pCANPackets[i].canFrame.ui32ID = 0x123;        // Address
                canBlock.pCANPackets[i].dwTimeout = 100;                // Write timeout
            }
            BOOL bRet = FlexCANTransfer(1, &canBlock);
            LOGMSG(ZONE_INFO, L"FlexCANTransfer result: %s\r\n", bRet ? L"TRUE" : L"FALSE");
            for (DWORD i=0; i<canBlock.dwNumPackets; i++)
                LOGMSG(ZONE_INFO, L"Individual transmit %d transmit result: %d\r\n", i, canBlock.pCANPackets[i].dwError);
        }

        // Now write a frame after receiving a remote request with the same ID
        canFrame.bExtended = CAN_EXTENDED;
        canFrame.btData[0] = 0x12;
        canFrame.btData[1] = 0x11;
        canFrame.btData[2] = 0x10;
        canFrame.btDataLen = 3;
        canFrame.ui32ID = 0x7654321;
        LOGMSG(ZONE_INFO, L"Waiting for remote request with ID 0x%08X...\r\n", canFrame.ui32ID);
        dwRet = FlexCANWriteFrame(1, CAN_DATA, CAN_TXONREMOTERX, 0, &canFrame, 10000);
        LOGMSG(ZONE_INFO, L"Frame with ID 0x%08X and data %s sent. Result: %d\r\n", canFrame.ui32ID, DataToHexString(&canFrame), dwRet);

        // Always exit out of loop
        break;
    }

    //Clean up
    FlexCANDeinit(1);

    if (hFinishEvent)
        CloseHandle(hFinishEvent);
    if (hReadThread)
        CloseHandle(hReadThread);

    DeleteCriticalSection(&gCS);

    return 0;
}

The code above is using our standard FlexCAN driver functionality and uses our (free downloadable) debug message templates for debug logging. Our High Performance FlexCAN driver supports complex ID filtering setups and a high speed (virtually unlimited) receive FIFO, among many more features.

Load, unload or reload a driver at runtime

If you develop drivers for Windows CE or Embedded Compact you know it is a major PITA to have to reload the entire kernel every time you change something in the driver code. You need to do this even in a debug kernel because the driver binaries are in use (and thus you can't re-compile & link) when the driver is loaded.

To workaround this I created a tool that allows you to unload or (re)load a driver at runtime. The way I use it is as follows:

  1. I build a small debug kernel including the driver under development
  2. I add the driver dll to the list of modules that will be loaded from the Release Directory (in VS2K5 with PB go to menu "Target"->"Release Directory Modules..." and add the driver)
  3. I add my tool (see below) as a subproject to the OS Design

Now when I download the kernel the driver will load (if the correct Drivers\BuiltIn registry values are there of course). I can set breakpoints in and step through my driver code, etc. Now when I find a bug I just press F5 to continue execution, open a "Target Control" window (menu Target->Target Control) and use my tool to unload the driver, then fix the bug, recompile & link and then (re)load the driver without the need for rebuilding or even reloading the kernel:

reloaddrv [-u] drv1: [[-u] drv2: {etc}]

In Target Control you use the 's' command to start an executable: s reloaddrv -u drv1:

The drv1: is the name of your driver (a 3 letter prefix followed by an index and a colon). Make sure to specify the -u (unload) parameter before the driver name!

To just reload the driver (so a quick unload followed immediately by a load; great for debugging initialization and clean-up) you use the tool as follows: s reloaddrv drv1:

If the driver was not loaded when you executed the above command the tool will try to find the BuiltIn registry key for the driver and load it from there.

You can specify multiple drivers on the commandline as well (-u always applies to the driver directly following it): s reloaddrv -u drv1: -u drv2: drv3: -u drv4: drv5: drv6:

I'm sure this tool is useful to a lot of you developing drivers for Windows CE/Embedded Compact, so here it is:

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPWSTR    lpCmdLine,
                   int       nCmdShow)
{
    BOOL bLoad = TRUE;
    LPWSTR pszToken = wcstok(lpCmdLine, L" ");
    while (pszToken)
    {   // Process parameters
        if (wcsicmp(pszToken, L"-u")==0)
        {   // Reset load flag if only unload requested
            bLoad = FALSE;
            pszToken = wcstok(NULL, L" ");
            continue;
        }

        // See if the driver is loaded
        DEVMGR_DEVICE_INFORMATION devInfo = {0};
        LPWSTR pszDeviceKey = NULL;
        devInfo.dwSize = sizeof(devInfo);
        HANDLE hFind = FindFirstDevice(DeviceSearchByLegacyName, pszToken, &devInfo);
        if (INVALID_HANDLE_VALUE != hFind)
        {   // Clean up find handle
            FindClose(hFind);
            // Store driver registry key
            pszDeviceKey = wcsdup(devInfo.szDeviceKey);
            // Unload driver
            RETAILMSG(1, (L"Found driver \"%s\", unloading...", devInfo.szLegacyName));
            DeactivateDevice(devInfo.hDevice);
        }
        else
            RETAILMSG(1, (L"Driver \"%s\" not loaded.", pszToken));

        if (bLoad)
        {   // Load driver if requested
            if (NULL == pszDeviceKey)
            {   // Try to find driver key in registry
                WCHAR szPrefix[4] = {0};
                DWORD dwIndex = 0;
                wcsncpy(szPrefix, pszToken, 3);
                dwIndex = _wtol(&pszToken[3]);
                HKEY hKey;
                BOOL bFound = FALSE;
                if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Drivers\\BuiltIn", 0, NULL, &hKey))
                {   // Opened BuiltIn drivers key
                    DWORD i = 0;
                    WCHAR szDriverKey[MAX_PATH];
                    DWORD dwCount = _countof(szDriverKey);
                    while (ERROR_NO_MORE_ITEMS != RegEnumKeyEx(hKey, i++, szDriverKey, &dwCount, NULL, NULL, NULL, NULL))
                    {   // Enumerate all driver keys
                        HKEY hDriverKey;
                        if (ERROR_SUCCESS == RegOpenKeyEx(hKey, szDriverKey, 0, NULL, &hDriverKey))
                        {   // Check if the prefix matches the specified one
                            WCHAR szDriverPrefix[MAX_PATH] = {0};
                            dwCount = sizeof(szDriverPrefix);
                            RegQueryValueEx(hDriverKey, L"Prefix", NULL, NULL, (LPBYTE)szDriverPrefix, &dwCount);
                            if (0 == wcsicmp(szPrefix, szDriverPrefix))
                            {   // And check if the index matches the specified one
                                DWORD dwDriverIndex = 0;
                                dwCount = sizeof(dwDriverIndex);
                                RegQueryValueEx(hDriverKey, L"Index", NULL, NULL, (LPBYTE)&dwDriverIndex, &dwCount);
                                if (dwDriverIndex == dwIndex)
                                {   // We found our driver key!
                                    bFound = TRUE;
                                    RegCloseKey(hDriverKey);
                                    break;
                                }
                            }
                            RegCloseKey(hDriverKey);
                        }
                        dwCount = _countof(szDriverKey);
                    }
                    if (bFound)
                    {   // Copy driver key name into pszDeviceKey
                        pszDeviceKey = (LPWSTR)malloc(MAX_PATH * sizeof(WCHAR));
                        if (pszDeviceKey)
                            wsprintf(pszDeviceKey, L"Drivers\\BuiltIn\\%s", szDriverKey);
                    }
                    else
                        RETAILMSG(1, (L"Can't find driver key for \"%s\"...", pszToken));
                    RegCloseKey(hKey);
                }
            }
            if (pszDeviceKey)
            {   // Reload driver
                RETAILMSG(1, (L"Reloading from \"%s\"...", pszDeviceKey));
                ActivateDeviceEx(pszDeviceKey, NULL, 0, NULL);
            }
        }
        // Clean up and get ready for next parameter
        if (pszDeviceKey)
            free(pszDeviceKey);
        bLoad = TRUE;
        pszToken = wcstok(NULL, L" ");
    }
    return 0;
}

And for your convenience here's the tool 7-zipped up as an OS Design subproject: reloaddrv.7z

Customizing a UI Component

In this blog post I will show you how you can customize all Windows CE dialogs without the need to change ANY file in the PUBLIC tree. As you no doubt know changing any code in the Windows CE PUBLIC (& PRIVATE) tree is a bad idea. It is a bad idea because it is very hard to maintain, your changes may be overwritten at any time by a Windows CE Update and worst of all your changes affect ALL OS Designs (not just the OS Design you need the changes for). Besides that you often need to do a "Build & Sysgen" to get your changes into your kernel image and we all know you should NEVER EVER do a "Build & Sysgen".

However, MSDN is still full of articles with instructions telling you to change code in the PUBLIC tree. An example of this is the "Customizing a UI Component" article in MSDN. It talks about modifying files in %_WINCEROOT%\Public\Common\Oak\Drivers and changing \WINCE600\PUBLIC\CEBASE\OAK\MISC\Winceos.bat... Bad, bad, bad!

Time for an update on this article:

Customizing a UI Component

To suit the requirements of your target device, Windows CE allows you to customize and replace certain user interface (UI) components. Windows CE provides these UI components in the form of libraries that you can either use as supplied or replace with your own custom UI components. The following table shows these UI components.

Component Description
Oomui Controls the appearance and behavior of your target device in out of memory conditions.
Startui Controls the appearance and behavior of system startup windows.
Calibrui Controls the appearance and behavior of your target device during calibration of the touch screen.

For detailed instructions on how to clone Calibrui please see Cloning CalibrUi in Windows CE 6.0. This article deals with STARTUI and OOMUI

To customize a UI component, we first need to clone the component into our OS Design and change it so that the build system uses our cloned binaries when linking GWES. We'll take STARTUI as an example for this exercise:

  1. Copy \WINCE600\PUBLIC\COMMON\OAK\DRIVERS\STARTUI to \WINCE600\OSDesigns\YourOSDesign\YourOSDesign\STARTUI
  2. Open the 'sources.' file in \WINCE600\OSDesigns\YourOSDesign\YourOSDesign\STARTUI and add the following to the top of the file (right under the !endif):
  3. _OEMINCPATH=$(_WINCEROOT)\public\common\ddk\inc;$(_WINCEROOT)\public\common\oak\inc;$(_WINCEROOT)\public\common\sdk\inc;
    __PROJROOT=$(_PROJECTROOT)
    _COMMONPUBROOT=$(_WINCEROOT)\public\common

    WINCEOEM=1
    PBP_PRESYSGEN=1

  4. Append '_clone' to the TARGETNAME:
  5. TARGETNAME=startui_clone

  6. Add RELEASETYPE and TARGET_PDB_NAME under TARGETTYPE=LIBRARY:
  7. RELEASETYPE=OAK
    TARGET_PDB_NAME=$(_RELEASELIBDIR)\$(TARGETNAME).pdb
  8. Remove the INCLUDES. In this case nothing is included from ..\..\inc but if it would be we would have to include the correct absolute path to the required include files.
  9. Remove any #xref lines (not used anymore since CE 2.12)
  10. The final sources file should look something like this:

    _OEMINCPATH=$(_WINCEROOT)\public\common\ddk\inc;$(_WINCEROOT)\public\common\oak\inc;$(_WINCEROOT)\public\common\sdk\inc;
    __PROJROOT=$(_PROJECTROOT)
    _COMMONPUBROOT=$(_WINCEROOT)\public\common

    WINCEOEM=1
    PBP_PRESYSGEN=1

    TARGETNAME=startui_clone
    TARGETTYPE=LIBRARY
    RELEASETYPE=OAK
    TARGET_PDB_NAME=$(_RELEASELIBDIR)\$(TARGETNAME).pdb

    MODULES=gwes
    CDEFINES= $(CDEFINES) -D__USERDECL_H__ -D__PRIVDECL_H__ -DGWE

    !IFDEF MEM_ACCOUNT
    CDEFINES=$(CDEFINES) -DMEM_ACCOUNT=1
    !ENDIF

    WINCETARGETFILES= \
      $(_COMMONOAKROOT)\lib\$(_CPUINDPATH)\$(TARGETNAME).res \
     
    SOURCES=startui.cpp                

  11. Now add this project to your OS Design's subprojects
  12. by right clicking the "SubProjects" node in the Solution Explorer and choosing "Add existing subproject...". Then browse to the STARTUI folder in your OS Design folder and be sure to select "Sources/Dirs Files (sources;dirs)" in the "Files of type" drop down list to be able to select the STARTUI sources. file.

    This action creates several files and adds several lines to our sources file that we do not need...

  13. From the Solution Explorer select all the files in the "Parameter Files" node (ProjSysgen.bat, STARTUI.bib, .dat, .db & .reg) of the STARTUI subproject, right click and "Remove and delete" them.
  14. If you want you can add "startui.rc" and "windowsce.bmp" to the "Resource Files" node in the subproject (but it is not necessary for building).
  15. Now we need to tell Platform Builder that this component needs to built before we sysgen the common tree so GWES can link our binaries instead of the default ones. We do this by editing the STARTUI.pbpxml file. Make sure the STARTUI.pbpxml file is as follows:
    <?xml version="1.0"?>
    <PBProject DisplayName="STARTUI (clone)" SysgenDeptree="common" SysgenModule="gwe2" xmlns="urn:PBProject-schema" />

    I added "(clone)" to the display name just to make clear this is a cloned component; it is not necessary for building. SysgenDeptree tells Platfom Builder this needs to be built before sysgenning the common dependency tree and SysgenModule specifies the module this component needs to be linked with.

  16. Since we named our project STARTUI_clone we need to rename the startui.rc file to startui_clone.rc (in preparation for the step below).
  17. The final step involves building the resources for all possible locales. The script used was taken from the Calibrui clone operation described in Cloning CalibrUi in Windows CE 6.0. The only thing added is setting the INCLUDE environment variable so that windows.h can be found by the resource compiler (startui_clone.rc needs windows.h). Copy the following into the prelink.bat file:
    @echo off
    @REM This is called before build to create all the RES files needed for all locales
    @REM WARNING: Do not change the names of any files because SYSGENMAKE.EXE assumes their location for cloning
    (IF NOT EXIST obj mkdir obj || goto EXIT_ERROR
    (IF NOT EXIST obj\%_TGTCPU% mkdir obj\%_TGTCPU%) || goto EXIT_ERROR
    (IF NOT EXIST obj\%_TGTCPU%\%WINCEDEBUG% mkdir obj\%_TGTCPU%\%WINCEDEBUG%) || goto EXIT_ERROR

    dir /AD /B %_PUBLICROOT%\COMMON\OAK\LIB\%_TGTCPU%\%WINCEDEBUG% > obj\%_TGTCPU%\%WINCEDEBUG%\clone_locales.txt

    set INCLUDE=%_WINCEROOT%\public\common\sdk\inc;
    set _PRELINK_LOCALE=%LOCALE%
    for /f %%L in (obj\%_TGTCPU%\%WINCEDEBUG%\clone_locales.txt) do call :COMPILE_RC %%L
    set LOCALE=%_PRELINK_LOCALE%
    goto :END_CLONING

    :COMPILE_RC
    (set LOCALE=%1 && nmake startui_clone.res) || goto EXIT_ERROR
    (IF NOT EXIST obj\%_TGTCPU%\%WINCEDEBUG%\%1 mkdir obj\%_TGTCPU%\%WINCEDEBUG%\%1) || goto EXIT_ERROR
    move /Y startui_clone.res obj\%_TGTCPU%\%WINCEDEBUG%\%1\startui_clone.res || goto EXIT_ERROR
    goto EOF

    :EXIT_ERROR
    exit /b -1

    :END_CLONING
    @REM Place any additional steps after END_CLONING but before EOF
    :EOF

That's it! You can now modify the STARTUI sources and build (sysgen) your OS Design to see your changes included in GWES, all without modifying one single file in the PUBLIC tree. The same instructions can be used to clone and modify OOMUI (the "out of memory" dialogs).

Good luck!

dwNKWatchDogThreadPriority

Windows CE supports a hardware watchdog implementation. The watchdog's properties can be set through two exported kernel variables; dwOEMWatchDogPeriod (to set the period in ms between feeding the dog) and dwNKWatchDogThreadPriority (to set the priority of the watchdog thread).

Unfortunately, dwNKWatchDogThreadPriority is not defined... Looks like somebody at Microsoft made a typo; the name of the exported variable is dwdwNKWatchDogThreadPriority (note the double 'dw').

Here's some example code to setup the watchdog:

extern DWORD dwOEMWatchDogPeriod;
extern DWORD dwdwNKWatchDogThreadPriority;

dwOEMWatchDogPeriod = 5000;
dwdwNKWatchDogThreadPriority = 100;
pfnOEMRefreshWatchDog = RefreshWatchdogTimer;

void RefreshWatchdogTimer(void)
{
    static BOOL bFirstTime = TRUE;
    OALMSG(OAL_FUNC, (L"+RefreshWatchdogTimer\r\n"));
    if (bFirstTime)
    {
        OALMSG(OAL_FUNC, (L"+RefreshWatchdogTimer: First call; init the Watchdog to timeout reset in %d secs\r\n", WD_RESET_PERIOD/1000));
        WatchdogInit(10000);
        bFirstTime = FALSE;
    }
    else
    {
        OALMSG(OAL_FUNC, (L"+RefreshWatchdogTimer: Subsequent calls; refresh the Watchdog timeout to %d secs again\r\n", WD_RESET_PERIOD/1000));
        WatchdogFeed();
    }
    OALMSG(OAL_FUNC, (L"-RefreshWatchdogTimer\r\n"));
}

Of course WatchdogInit and WatchdogFeed are functions implemented to initialize and feed your hardware watchdog.

Windows Embedded QFE's (updates)

Since the change to Silverlight/Bing on Microsoft downloads, the download search has become completely useless (for fun, try searching for "CE 6.0", then try "CE") [Update: MS seems to have fixed the search phrase problems, so "CE" and "CE 6.0" now return the correct results. Nevertheless; the remainder of this blog post is still valid]. Therefore the links in this post do not work anymore and an update is needed.

All Windows Embedded Compact 7 / Windows Embedded CE 6.0 / Windows Embedded CE 5.0 / Windows CE.NET 4.2 / Windows CE 3.0 (you can leave it to MS marketing to make product naming as confusing as possible...) updates can be found at these links:

Windows Embedded Compact 7
Windows Embedded CE 6.0
Windows Embedded CE 5.0
Windows CE .NET 4.2
Windows CE 3.0

You can still download the QFEInstaller so you don't have to click the QFE installation wizard a million times, but nowadays the msi installers support silent installation through commandline options as well. The QFEInstaller is still handy because it makes sure the QFE's are installed in the right order.

Syndicate content