Flash device driver in Windows Embedded CE 6.0

In previous version of Windows CE the flash drivers followed a model based on FAL (Flash Abstraction Layer): the developer had to write the FMD (Flash Media Driver) layer and link it with fal.lib to create a DLL which exposed a block (DSK_Xxx) stream interface. Starting with Windows Embedded CE 6.0 R2 MS introduces a new model based on a MDD/PDD layer structure. The MDD part is implemented in a DLL (flashmdd.dll) exposing a block (DSK_Xxx) stream interface which handles some specific IOCTL_FLASH_XXX codes; the DLL calls the PDD DLL which the developer has to write according to the interface specified by MS; this interface is similar to the MDD (a block stream interface driver) which handles some specific IOCTL_FLASH_PDD_XXX codes. The FAL/FMD model is still supported.
In addition, in Windows Embedded CE 6.0, you have a new partion driver (flashpart.dll) specifically designed for flash block devices. This partition driver has to be used with the MDD DLL since it's aware of the specific IOCTL_FLASH_XXX codes that the MDD DLL implements.

MS provides a library (flashpddwrapper.lib) which gives the developer the opportunity to reuse the FMD code in the new architecture: this library exposes the PDD interface to the MDD DLL but internally calls the old FMD entry point. 

Notice that the shared source for Windows Embedded CE 6.0 includes the FAL library source code which was not available before: the FAL is implemented in C++  while in previous versions it was implemented in C. Other source code which is available (in the public part of %_WINCEROOT%) includes flashpddwrapper and some specific FMD. Some of these FMD (for example the sample RAM flash driver) are linked in two different ways to create a flash driver which uses the FAL (for example ramfmd) and another one which benefits from the new model (flashpdd_ram). 

Flash driver architecture before Windows Embedded CE 6.0 R2

FAL.LIB + FMD.LIB (developer)

Flash driver architecture after Windows Embedded CE 6.0 R2

MDD/PDD

FLASHMDD.DLL

FLASHPDD.DLL (developer)


MDD/PDD wrapping the FMD

FLASHMDD.DLL

FLASHPDD.DLL (FLASHPDDWRAPPER.LIB + FMD.LIB (developer))


FAL/FMD 

FAL.LIB + FMD.LIB (developer)

For additional info see MSDN documentation.

Get last error and set it meaningfully

Very often I read posts in the newsgroups like:

I call function Foo(dwParam) and it returns FALSE. What's wrong?

If I cannot tell it immediately -for example because the dwParam is clearly wrong - my standard answer is:

Have you called GetLastError()? Which error value it returns?

GetLastError (if the call that fails actually sets an error and the error is meaningful) can be very helpful to detect why the call is failing. If you do not understand exactly what the code means and you have the code you may figure out where and why the failing function is calling SetLastError with that specific code.

Take in account that the error value you get is per thread -the kernel structure modeling the thread has a DWORD member named dwLastError: Since GetLastError retrieves the latest error this means that, if the failing function you are investigating does not uses SetLastError(), the error you get is related to a previous call to another function so can be misleading (or it can be 0 since a function you call before succeded and set the error to ERROR_SUCCESS.

The error codes are defined in the documentation but, for a more complete list and meaning you can check  %_WINCEROOT%\PUBLIC\COMMON\SDK\INC\winerror.h.

When you write your own code is a good practice to use SetLastError(dwError) with a meaningful error value; if you do not find an adapted system value for the error you can define your own: an error code which has bit 29 set  is guaranteed not to conflict with any system error code.

The error codes are DWORD's which should be formatted as follows - according to winerror.h:

Bits 31 & 30 are the severity code where

          00 - Success
          01 - Informational
          10 - Warning
          11 - Error

Bit 29 is the Customer code flag

Bit 28 is a reserved bit

Bits 28-16 are the facility code,  which -more or less- identifies the subsytem generating the error, Internet, Storage, etc

Bits 15-0 are the actual error code

 

Take in account that the severity code is almost not used in winerror.h codes: for example ERROR_INVALID_PARAMETER which is undoubtly an error (neither a warning nor an information) has a value of 0x57L, not 0xC0000057L.

On the contrary the severity bits are used in Windows NT-based OS NTSTATUS error codes: for example STATUS_UNSUCCESSFUL which is the more generic error that you will have has a value of 0xC0000001L.

By the way, most DDK functions returns an NTSTATUS code so there's no need to call a function like GetLastError to have more information about the error itself.

Windows Embedded CE 6.0 R3 released

Windows Embedded CE 6.0 R3 is finally available for download here. This intermediate release (while we wait for Windows Embedded Compact in 2010) brings several features, the most important of which is probably Silverlight for Windows Embedded. Download a free trial of Expression Blend to create new user interface experience for your embedded device!

For more info on the features included in CE 6.0 R3 check the following links:

Note that Office and PDF viewers for Windows Embedded CE 6.0 are available as a separate download here while Mobile QQ Messenger Client can be downloaded here (both the packages are from 3rd party providers).

CE 6.0 R3 includes the Adobe Flash Lite Plug-In for IE browser: if you are interested in developing Flash applications you can find a lot of info and resources from Adobe site.

 

A curious issue with PCI bus driver & hive based registry

Recently we faced a strange problem on one of our x86 systems: after some successfull boots the system was unable to start the OS anymore but deleting the 'Documents and Settings' folder on the storage device (we included the hive based registry in the OS) the system behave normally.

After some investigation we found out that when the system was not booting correctly the USB OHCI driver was performing an endless loop waiting for the host controller to reset (thus preventing the device manager to load the other drivers - never
write an XXX_Init routine which can spin forever!).

The code looks like:

m_portBase->HcCommandStatus.HCR = 1;

while (m_portBase->HcCommandStatus.HCR == 1)
   ;
 
With the debugger we found out that all the memory area used to map the USB controller registers was 0xFF (!), thus the bit was never cleared. This looked very strange so we started looking at the system registry founding out that the BAR (Base Address Registry) of the USB host on the PCI bus was 0x80003000 while the device was actually mapped at 0xDA000 (both flat physical addresses).

An additional information: our BIOS implements legacy USB support which means that the BIOS maps the USB host at 0x80003000 when it assigns the resources to the PCI bus. If the legacy USB support is enabled the BIOS maps the USB host at 0xDA000 (under the 1MB limit), configures it and tries to find possibly connected manageable USB devices (i.e. storage and keyboard). If such devices are not detected the controller is remapped back at 0x80003000 otherwise it remains at 0xDA000 (this allow the BIOS to boot the OS from a USB disk redirecting INT13 on the USB - but this is another story...). 

After accusing one collegue of mine (he wrote the BIOS after all!) we realized that you could trigger the problem this way:

  • Connect the USB keyboard to the system, turn it on and boot a clean OS (no hives on the storage).
  • Turn off the system, disconnect the keyboard and turn the system on: the OS does not boot anymore.

or

  • Turn on the system (no USB keyboard) and boot a clean OS (no hives on the storage).
  • Turn off the system, connect the keyboard and turn the system on: the OS does not boot anymore.

Looking at the PCIBUS code the problem became clear: on a cold boot the bus driver enumerates the PCI devices and build the  'Instance' keys based on the 'Template' keys. Under the 'Instance' key the bus driver saves relevant informations like the SysIntr value and the BAR's (look here for more information).

The PCIBUS driver is quite smart so, in subsequent boots, it enumerates the PCI devices and analyze the 'Instance' keys (using the hive based registry they're already set up) first - this allow the bus driver not to load a driver for a device which is not present anymore. If it founds that there is already an 'Instance' key for a specific device (with matching PCI ID's and location in terms of bus/device/function) it says "Ha ha! Here it is, do not bother to recreate the 'Instance' key and all its values for this device, neither the BAR value".

Do you see the problem? When you boot the first time with the USB keyboard connected the BIOS maps the USB host at 0xDA000 which is the address saved in the hive under the 'Instance' key. Some time later you will turn on the system without the keyboard, the BIOS will map the host at 0x80003000 but the USB host device driver will retrieve the wrong 0xDA000 BAR from the registry, thus accessing unused RAM instead of the controller registers.

I cloned the PCIBUS and modified it so that it ignores the 'Instance' key for PCI USB hosts, deletes it and recreates it at every boot based on the 'Template' key. 

 

  

Windows Embedded CE 6.0 KITL in PQOAL

In Windows Embedded CE 6.0, the kernel and OEM code are divided into the following three components:
  • oal.exe (nk.exe): Startup code and the OEM adaptation layer (OAL) implementation
  • kernel.dll: OAL-independent kernel implementation
  • KITL.dll: This contains the platform-specific Kernel Independent Transport Layer support.

In previous versions the OAL and the Kernel where linked together in one executable (kern.exe) which would possibly include KITL support (in this case the executable was kernkitl.exe).

From Windows CE 5.0 Microsoft introduced the concept of Production Quality OAL : one of the benefits of this design is that you can take advantage from a set of libraries which can be shared among specific BSP’s.

For example, if you look at %_WINCEROOT%\PLATFORM\COMMON\SRC tree you can find a directory structure like the following:

  • COMMON: common code which is processor architecture independent.
  • ARM, MIPS, SHX, X86: code which depends on the specific processor architecture but not on the specific implementation.
  • SOC: code which depends on the specific processor

With this structure the single BSP should contain only the code which is really specific for the specific board.

The PQOAL architecture extends even to KITL: in this scenario KITL.dll is made up essentially by the following components:

  • BSP specific KITL code (built from %_WINCEROOT%\PLATFORM\<BSP>\KITL)
  • KITL specific device code (built from %_WINCEROOT%\PLATFORM\<BSP>\SRC\COMMON\ETHDBG) (see NOTE 1)
  • Common OAL KITL library (oal_kitl.lib, built from %_WINCEROOT%\PLATFORM\COMMON\SRC\COMMON\KITL)
  • KITL core library (kitlcore.lib, built from %_WINCEROOT%\PRIVATE\WINCEOS\COREOS\NK\KITL)

 

Fig 1: KITL.DLL

1. OEMInit()

This function, called by the kernel, invokes KITLIoctl (IOCTL_KITL_STARTUP, NULL, 0, NULL, 0, NULL) which simply calls OEMKitlStartup()

2. OEMKitlStartup()

The function sets up:

  • KITL device name
  • OAL_KITL_ARGS structure: retrieved from data – normally called BSP_ARGS (see NOTE 1)   setup by the boot loader since the KITL device is normally the same used by the boot loader to download the OS image, or hardcoded in the OAL. The structure contains:
    • KITL flags describing some KITL functionalities (use interrupt or polling, etc.)
    • Location of the KITL hardware device in terms of its interface, bus number, and logical location
    • Communication parameters: for a serial interface baud rate, parity, etc. ; for an ethernet interface MAC, IP, ...

These information are passed to OALKitlInit with a NULL terminated array of OAL_KITL_DEVICE structures – normally called g_kitlDevices (see NOTE 1); each element defines one of the available KITL devices on the platform; each device is represented by its type (OAL_KITL_TYPE_SERIAL, OAL_KITL_TYPE_ETH,...), physical location and by a structure which defines the function pointers to handle the KITL device according to the device type: for example if KITL device is an ethernet chip the structure holds a pointer to an OAL_KITL_ETH_DRIVER structure. These entry points are typically shared among KITL and the boot loader and are defined in %_WINCEROOT%\PLATFORM\<BSP>\SRC\COMMON\ETHDBG.

3. OALKitlInit(LPCSTR deviceId, OAL_KITL_ARGS *pArgs, OAL_KITL_DEVICE *pDevice)

This function, if KITL flags include OAL_KITL_FLAGS_ENABLED, calls OALKitlFindDevice(DEVICE_LOCATION *pDevLoc, OAL_KITL_DEVICE *pDevice) which checks if the KITL device specified in OAL_KITL_ARGS is supported - i.e. it is present in the OAL_KITL_DEVICE array; if this is the case KitlInit is called.

4. KitlInit(BOOL fStartKitl)

This function, if fStartKitl is TRUE (which means that OAL_KITL_FLAGS_PASSIVE flag is not set) calls StartKitl(TRUE).

5. StartKitl (BOOL fInit)

This function calls OEMKitlInit to initialize a KITLTRANSPORT structure;

6. OEMKitlInit(PKITLTRANSPORT pKitl)

This function calls the KITL device initialization function according to the KITL device type (for example OALKitlEthInit if the device is OAL_KITL_TYPE_ETH or OALKitlSerialInit if the device is OAL_KITL_TYPE_SERIAL). The function initializes the KITL hardware and fills the KITLTRANSPORT structure with pointers to functions which are used to perform several actions (for example send or receive a KITL frame) over the specific transport: remember that KITL stands for Kernel Independent Transport Layer so using the KITLTRANSPORT pointers to functions abstracts the transport from the specific device.

7. The control is back to StartKitl which tries to connect to the desktop (i.e. Platform Builder). If the connection is established the function tries to enable the interrupt handling for the KITL device to achieve a better performance. The interrupt handler is not used if you specified KITL_SYSINTR_NOINTR in KITLTRANSPORT::Interrupt or if you specified that KITL has to run in passive mode.

8. Back from StartKitl the KITL has been setup and the control is finally back to OEMInit the to the kernel which will continue to boot the operating system.

 

With the PQOAL design the BSP specific code required to setup KITL may be reduced to implement OEMKitlStartup and the functions used to communicate with the actual KITL device. In some case you may need to customize the common KITL library: for example CEPC KITL adds to the common KITL library the support for PCI since the ethernet device on an x86 board will be probably interfaced on that bus

NOTE 0: This post is about KITL initialization at system startup. When KITL reinitializes after a suspend/resume cycle some steps are skipped or performed in a different way

NOTE 1: This name is typically used but it is not mandatory

Backlight Management

Many Windows Embedded CE device drivers have a defined interface: they can be stream interface driver with a well defined MDD/PDD layer like audio drivers; or drivers which have a  MDD/PDD layer but do not expose a stream interface, like touch screen drivers; another type of device driver is network adapter miniport drivers which must implement some defined functions to interact with NDIS; and there are other examples.

There is no specification about how a backlight driver has to be designed so you have to design it yourself, maybe taking a look to implementations of the various BSP's shipped with Platform Builder (for example, in VOIP_PXA270 BSP folder you can find a backlight stream interface driver which has been implemented with a MDD/PDD design in mind).

A simple backlight management can be embedded in the display device driver: the code which handles the power IOCTL's
could turn off the backlight if the display is requested to go to D4 state and turn it on for higher consumption power states; n
evertheless this does not handle all the options that Windows CE control panels offer to the user.

Despite of what the documentation says you have nothing special to do to include backlight support in the control panel since it's included by default; possibly you have to modify %_WINCEROOT%\PUBLIC\CEBASE\OAK\MISC\wceshellfe.bat if you do not want to support the backlight user interface removing backlight from %CPLMAIN_COMPONENTS% environment variable.

The Screen control panel applet allows to configure the backlight behaviour with different settings when the device is powered by an AC supply or when it's powered by a battery. In both cases the user can select to keep the backlight always on or to turn it off after a specified amount of time that the device is not in use (on WM devices you can decide to turn on the backlight if the user taps the screen or presses a button).

Every time the user changes one of these settings the screen control panel applet saves it to the registry under [HKEY_CURRENT_USER\ControlPanel\Backlight] key (note that the settings are per user).
Registry values are defined in %_WINCEROOT%\PUBLIC\WCESHELLFE\OAK\CTLPNL\CPLMAIN\regcpl.h and the most relevant are the following:

"UseBattery"=dword             ;if '1' turn off the backligh after "BatteryTimeout" seconds
"UseExt"=dword:                   ;if '1' turn off the backligh after "ACTimeout" seconds
"BatteryTimeout"=dword:X ;backlight timeout in seconds
"ACTimeout"=dword:Y        ;backlight timeout in seconds

In addition when a value is modified the screen control panel applet sets the  named event "BackLightChangeEvent" to warn about the change.

If you want to expose custom or advanced backlight settings to the user you can implement an advanced backlight control panel applet (for example I did it to allow the user to adjust the backlight level moving a trackbar): the applet must be implemented in a DLL which exports a function with this name and prototype:

BOOL BacklightAdvApplet(
  HWND hDlg
);

If you add to the backlight registry key [HKEY_CURRENT_USER\ControlPanel\BackLight] the string value "AdvancedCPL" which holds the name of the DLL which implements the applet, the screen control panel applet will show an 'Advanced...' button in the backlight tab. When the user clicks the button the BacklightAdvApplet function will be called.
You can find a sample advanced applet under %_WINCEROOT%\PUBLIC\WCESHELLFE\OAK\CTLPNL\ADVBACKLIGHT.

Instead of the simple backlight management decribed in the first paragraph, the following code shows as a thread can be used to manage the backlight taking in account the user settings from the control panel.

 //Warning: this code has not be tested; for simplicity there's no error handling or check on functions return values 
#include <windows.h>
#include <ceddk.h>
#define REGISTRY_CHANGE_EVENT_SZ _T("BackLightChangeEvent")  
#define USER_ACTIVITY_EVENT_SZ  _T("PowerManager/UserActivity_Active")
 
#define USER_ACTIVITY_EVENT  0
#define POWER_CHANGE_EVENT  1
#define REGISTRY_CHANGE_EVENT 2  
 
//Helper function to retrieve values from [HKEY_CURRENT_USER\ControlPanel\Backlight]; not implemented
BOOL GetRegistrySettings(DWORD * pdwUseExt, DWORD * pdwUseBattery, DWORD * pdwACTimeout, DWORD * pdwBatteryTimeout);
//Helper function to enable/disable the backlight; returns the backlight status just set; not implemented
BOOL EnableBacklight(BOOL fOn);
DWORD WINAPI BacklightControlThread(LPVOID lpParam)
{
 //Backlight timeout
 DWORD      dwBacklightTimeout; 
 //Current power status, AC / battery
 BOOL      fACLineStatus;  
 //Current backlight status
 BOOL      fBacklightOn; 
 //Current settings
 DWORD      dwUseExt, dwUseBattery, dwACTimeout, dwBatteryTimeout;
 //
 DWORD      dwWaitStatus, dwBytesRead, dwFlags;
 //For power status change notifications
 MSGQUEUEOPTIONS    MsgQueueOpt;
 UCHAR      PowerInfoBuffer[sizeof(POWER_BROADCAST)+sizeof(PBT_POWERINFOCHANGE)];
 PPOWER_BROADCAST   pPowerInfo = (PPOWER_BROADCAST)PowerInfoBuffer;
 POWER_BROADCAST_POWER_INFO  *ppbpi;
 //Handles to the various events we wait on
 HANDLE       hEvents[3];
 SYSTEM_POWER_STATUS_EX  PowerStatus;
 
 
 //Enable the backlight at the beginning
 fBacklightOn = EnableBacklight(TRUE);
 
 //Get the current power supply status
 GetSystemPowerStatusEx(&PowerStatus,TRUE);
 
 fACLineStatus = (PowerStatus.ACLineStatus  & AC_LINE_ONLINE) ? TRUE : FALSE; 
 
 //Get the backlight settings
 GetRegistrySettings(&dwUseExt, &dwUseBattery, &dwACTimeout, &dwBatteryTimeout);
 //Evaluate the timeout
 dwBacklightTimeout = (fACLineStatus ? dwUseExt : dwUseBattery) ?  //Check if we must turn off according to power supply
  (fACLineStatus ? dwACTimeout : dwBatteryTimeout) :      //Set the timeout if we must turn off
  INFINITE;               //Otherwise set user activity timeout to INFINITE
 //Handle to user activity event signaled by the power manager.
 hEvents[USER_ACTIVITY_EVENT] = CreateEvent(NULL, FALSE, FALSE, USER_ACTIVITY_EVENT_SZ); 
 //Handle to notification for power status change
 MsgQueueOpt.dwSize = sizeof(MSGQUEUEOPTIONS);
 MsgQueueOpt.dwFlags =  MSGQUEUE_NOPRECOMMIT |  MSGQUEUE_ALLOW_BROKEN;
 MsgQueueOpt.dwMaxMessages = 0;
 MsgQueueOpt.cbMaxMessage = sizeof(POWER_BROADCAST) + sizeof(PBT_POWERINFOCHANGE);
 MsgQueueOpt.bReadAccess = TRUE;
 hEvents[POWER_CHANGE_EVENT] = CreateMsgQueue(NULL,&MsgQueueOpt);
 RequestPowerNotifications(hEvents[POWER_CHANGE_EVENT],PBT_POWERINFOCHANGE);
 
 //Handle to registry change event. Note that we use CreateEvent, not OpenEvent, since the screen control panel applet
 //actually creates the event only when it needs to set it then closes it
 hEvents[REGISTRY_CHANGE_EVENT] = CreateEvent(NULL, FALSE, FALSE, REGISTRY_CHANGE_EVENT_SZ);
 
 while(1)
 {
  dwWaitStatus = WaitForMultipleObjects(_countof(hEvents), hEvents, FALSE,
   (dwBacklightTimeout != INFINITE) ? dwBacklightTimeout*1000 : INFINITE);
   
  switch(dwWaitStatus)
  {
   //user activity timeout elapsed, turn off the backlight if it's on
   case WAIT_TIMEOUT:
   
    if(fBacklightOn)
     fBacklightOn = EnableBacklight(FALSE);
    break;
    
   //user activity, turn on the backlight if it's off 
   case WAIT_OBJECT_0 + USER_ACTIVITY_EVENT:
   
   
    if(!fBacklightOn)
     fBacklightOn = EnableBacklight(TRUE);
    
    break;
    
   //power change notification    
   case WAIT_OBJECT_0 + POWER_CHANGE_EVENT:
   
    //Get information about the power supply state
    ReadMsgQueue(hEvents[POWER_CHANGE_EVENT],&pPowerInfo,sizeof(PowerInfoBuffer),&dwBytesRead, 0, &dwFlags);
    ppbpi = (PPOWER_BROADCAST_POWER_INFO) &pPowerInfo->SystemPowerState[0];
    fACLineStatus = ppbpi->bACLineStatus;
    
    //Evaluate the timeout
    dwBacklightTimeout = (fACLineStatus ? dwUseExt : dwUseBattery) ? 
       (fACLineStatus ? dwACTimeout : dwBatteryTimeout) :   
       INFINITE;            
       
    //Consider a change in power supply as user activity so turn on the backlight      
    if(!fBacklightOn)
     fBacklightOn = EnableBacklight(TRUE);
     
    break;
    
   //registry change notification    
   case WAIT_OBJECT_0 + REGISTRY_CHANGE_EVENT:
   
    //Get the new backlight settings
    GetRegistrySettings(&dwUseExt, &dwUseBattery, &dwACTimeout, &dwBatteryTimeout);
   
    //Evaluate the timeout
    dwBacklightTimeout = (fACLineStatus ? dwUseExt : dwUseBattery) ? 
       (fACLineStatus ? dwACTimeout : dwBatteryTimeout) :   
       INFINITE; 
       
    //Enable the backlight if it's off       
    if(!fBacklightOn)
     fBacklightOn = EnableBacklight(TRUE);       
   
    break;
   case WAIT_FAILED:
   default:
    ;
  }
 }
}  

 

Integrating your project in the 3rdParty folder

A recent feedback by Bjoern Feld on my post about 'Integrating your project in the PUBLIC tree' leads me to write this new one. Once you have created your driver/application you want probably to distribute it to your customer; maybe you want to
install your product in the %_WINCEROOT%\3rdParty folder. In this example I will assume that ACME wants to distribute a device driver for a new rocket -which will hopefully help Wile E. Coyote to terminate the Road Runner.

Open a Platform Builder workspace you have on hand and, from Project menu choose 'Add new subproject...'. Select 'WCE Dynamic-Link library' name it 'ROCKET001' then choose to create an empty subproject.

In the folder you'll end up with the following files:

  • rocket001.bib
  • rocket001.dat
  • rocket001.db
  • rocket001.def
  • rocket001.pbpxml
  • rocket001.reg
  • makefile
  • postlink.bat
  • prelink.bat
  • ProjSysgen.bat
  • ReadMe.txt
  • sources    

Remove the prelink.bat, ProjSysgen.bat and ReadMe.txt which are not useful for the example then:

Modify the sources file content as following:

TARGETNAME=ROCKT001

SOURCES= \

TARGETTYPE=NOTARGET
POSTLINK_PASS_CMD=postlink.bat

Fill the postlink.bat file with this:

copy Target\%_TGTCPU%\%WINCEDEBUG%\ROCKT001.*  %_FLATRELEASEDIR%

This will copy the binary files to %_FLATRELEASEDIR%: I'm supposing that you will place a binary file for the rocket driver for several CPU architecures and for both the release and the debug build (just in case that Wile E. Coyote wanted to debug why the rocket blew up).

Create an ACME folder under %_WINCEROOT%\3rdParty and copy the ROCKT001 folder into it.

Create a catalog file rockt001.pbcxml for your project in  %_WINCEROOT%\PUBLIC\COMMON\CATALOG; the content of the file will be the following

<?xml version="1.0" encoding="utf-8"?>
<CatalogFile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" RequiredVersion="6.00" xsi:schemaLocation="urn:Microsoft.PlatformBuilder/Catalog PbcXml600.xsd" xmlns="urn:Microsoft.PlatformBuilder/Catalog">
  <FileInformation Id="FileInformation:ACME:Rocket001">
    <Title>ACME Rocket</Title>
    <Description>Anti-road-runner weapon</Description>
    <Vendor>ACME</Vendor>
    <OSVersion>6.00</OSVersion>
    <FileVersion>1.00</FileVersion>
  </FileInformation>
  <Item Id="ROCKET001">
    <Title>ACME Rocket v 001</Title>
    <Variable>BSP_ACME_ROCKET001</Variable>
    <Module>rockt001.dll</Module>
    <Project>$(_WINCEROOT)\3RDPARTY\ACME\ROCKT001\ROCKT001.PBPXML</Project>
    <Location>Device Drivers</Location>
  </Item>
</CatalogFile>

Ok, now you have all you need to distribute your binaries: the last but not least thing you have to do is create an .msi file to distribute your rocket driver: I will not consider here how to do it, but you can take a look here.

You can download the sample folder for the rocket driver here. It includes the catalog file that you have to copy under %_WINCEROOT%\PUBLIC\COMMON\CATALOG. I put only the ARMV4I retail binaries (which are actually related to a sample DLL).

 


 

Removing a default BSP component from a workspace

Every BSP has a batch file with the same name of the BSP itself: this file is invoked for every workspace based on that BSP instructing the development environment (for example) to add some components which the developer thought should be included by default on every workspace usng that BSP. For example in a BSP .bat you can find something like:

set BSP_USB_OHCI=1
set BSP_USB_UHCI=1
set BSP_USB_EHCI=1

which include in every workspace the support for the various flavours of USB host controllers.

Suppose that you want to create a workspace that does not include the EHCI support: if you try to remove the item from the workspace catalog you get a message like "if you want to remove this item you have to remove those other components, blah, blah, and you have to remove the environment variable 'BSP_USB_EHCI'".

You do not want to change the BSP .bat files since the change will involve all the workspaces based on that BSP.

How do you remove (or unset) an environment variable in Platform Builder?

The properties/settings GUI for your workspace allows you only to set an environment variable to a specific value but you cannot unset it. Setting a variable to 0 has no effet since the .bib and .reg files typically check if a variable is defined
or not.

Inspecting  %_WINCEROOT%\PUBLIC\COMMON\OAK\MISC\wince.bat file I found that after calling the BSP batch file, wince.bat calls (if it exists) a file called %_PROJECTROOT%\postWinCE.bat. Since this is a batch file you can use 'set BSP_USB_EHCI=' to unset the environment variable on a workspace by workspace base.

Invalid handle?

Several Windows Embedded CE API's deal with handles: an application obtains a handle to an object than it typically checks if the handle is valid or not; unfortunately there are two values which means invalid handle: for example CreateFile returns INVALID_HANDLE_VALUE (which is defined as -1 or 0xFFFFFFFF) while CreateEvent returns NULL.

A mnemonic aid is the following: all the API's that deal with files, storage and database (CreateFile, FindFirstChangeNotification, OpenStore, CeOpenDatabaseEx, ...) return INVALID_HANDLE_VALUE while the other API's return NULL.  

Some noticeable exceptions to this statement are:

  • CreateToolhelp32Snapshot
  • FindFirstDevice
  • CeFindFirstRegChange

which returns INVALID_HANDLE_VALUE.

Note that there is an error in Windows Embedded CE documentation about OpenDeviceKey: it is stated that the function returns INVALID_HANDLE_VALUE in case of error while it actually returns NULL.

Windows Embedded Standard 2009 MCTS Preparation Kit is online!

MS has released the preparation kit for Windows Embedded Standard 2009 MCTS exam: go to http://msdn.microsoft.com/en-us/embedded/dd439483.aspx and get it! It's a very useful tool both for the exam and to approch the subject. MS released also a similar kit for Windows Embedded CE MCTS (http://msdn.microsoft.com/en-us/embedded/cc294468.aspx)