Tuesday, November 16, 2010

Windows Phone 7 Programming Free Ebook


If you plan to start some development around Windows Phone 7 devices, I would suggest you to read this free ebook recently published :

Programming Windows Phone 7 by Charles Petzold (available here)


- Nicolas

Wednesday, November 3, 2010

Adeneo announces unified BSP for TI SoCs, partner awards - News - Windows for Devices

Adeneo announces unified BSP for TI SoCs, partner awards - News - Windows for Devices

- Nicolas

Driver wrapper for C#

From .NET Compact Framework, there is no API allowing access to the driver interfaces, as the main reason of the framework existance is to be able to run on whatever hardware an application without having to recompile it, this by the usage of the CLR (Common Language Runtime).

But developper working on embedded systems usually have to access those drivers that are specific to the platform. So in that case they need an access to the native APIs.

Identify the needs
Before getting access to the driver from C# you have to identify the APIs that have to be mapped from native to managed environment. Accessing a driver from native code is performed using the following APIs :
- CreateFile : in order to open a driver instance
- CloseHandle : to close the opened instance
- ReadFile : read data from the stream
- WriteFile : write data into the stream
- Seek : move data pointer in the stream
- DeviceIoControl : to perform driver specific actions with the usage of the CTL_CODE macro for commands IDs

Map the native API
In order to map the native API into C#, the usage of the interop services is required.
Note : you can find native API mapping from pinvoke.net (http://www.pinvoke.net/) website.

Create a driver class
We have to define the Driver class that will handle all the wrapping actions for us.


using System;
using System.Runtime.InteropServices;

namespace Adeneo_Embedded
{
public class Driver
{
}
}


Map the mandatory native functions

  • CreateFile function

// This function creates, opens, or truncates a file, communications
// resource, disk device, or console. It returns a handle that can be
// used to access the object. It can also open and return a handle to
// a directory.
[DllImport ("coredll.dll")]
private static extern int CreateFile(
string lpFileName,
int dwDesiredAccess,
int dwShareMode,
int lpSecurityAttributes,
int dwCreationDisposition,
int dwFlagsAndAttributes,
int hTemplateFile);


  • CloseHandle function

// This function closes an open object handle
[DllImport ("coredll.dll")]
private static extern int CloseHandle(int hObject);


  • ReadFile function

// This function reads data from a file, starting at the position indicated
// by the file pointer. After the read operation has been completed, the
// file pointer is adjusted by the number of bytes actually read.
[DllImport ("coredll.dll")]
private static extern int ReadFile(
int hFile,
byte[] lpBuffer,
int nNumberOfBytesToRead,
ref int lpNumberOfBytesRead,
ref OVERLAPPED lpOverlapped);


  • WriteFile function

// This function writes data to a file. WriteFile starts writing data to
// the file at the position indicated by the file pointer. After the write
// operation has been completed, the file pointer is adjusted by the number
// of bytes actually written.
[DllImport ("coredll.dll")]
private static extern int WriteFile(
int hFile,
byte[] lpBuffer,
int nNumberOfBytesToWrite,
ref int lpNumberOfBytesWritten,
ref OVERLAPPED lpOverlapped);


  • DeviceIoControl function

// This function sends an IOCTL directly to a specified device driver,
// causing the corresponding device to perform the specified operation.
[DllImport ("coredll.dll")]
private static extern int DeviceIoControl(
int hFile,
uint dwIoControlCode,
byte[] lpInBuffer,
uint nInBufferSize,
byte[] lpOutBuffer,
uint nOutBufferSize,
ref uint lpBytesReturned,
ref OVERLAPPED lpOverlapped);


Redefine the CTL_CODE macro
When driver developper is implementing the driver IoControls and when the application developper want to execute this command, then both should refer to the same identifier. To get a unique identifier for a driver command, the CTL_CODE macro is used.


//
// Macro definition for defining IOCTL and FSCTL function control codes. Note
// that function codes 0-2047 are reserved for Microsoft Corporation, and
// 2048-4095 are reserved for customers.
//

public uint CTL_CODE(uint DeviceType, uint Function, uint Method, uint Access )
{
}


Redefine the constant values
CreateFile, DeviceIoControl, and CTL_CODE code is using constant that also have to be redefined in our driver class.


#region "constants"
private const int GENERIC_READ = unchecked((int)0x80000000);
private const int GENERIC_WRITE = 0x40000000;
private const int OPEN_EXISTING = 3;
private const int INVALID_HANDLE_VALUE = -1;

#region "CTL_CODE"
#region "Method"
//
// Define the method codes for how buffers are passed for I/O and FS controls
//
public const uint METHOD_BUFFERED = 0;
public const uint METHOD_IN_DIRECT = 1;
public const uint METHOD_OUT_DIRECT = 2;
public const uint METHOD_NEITHER = 3;
#endregion // "Method"

#region "Access"
//
// Define the access check value for any access
//
//
// The FILE_READ_ACCESS and FILE_WRITE_ACCESS constants are also defined in
// ntioapi.h as FILE_READ_DATA and FILE_WRITE_DATA. The values for these
// constants *MUST* always be in sync.
//

public const uint FILE_ANY_ACCESS = 0;
public const uint FILE_READ_ACCESS = ( 0x0001 ); // file & pipe
public const uint FILE_WRITE_ACCESS = ( 0x0002 ); // file & pipe
#endregion // "Access"

#region "DeviceType"
// begin_ntddk begin_nthal begin_ntifs
//
// Define the various device type values. Note that values used by Microsoft
// Corporation are in the range 0-32767, and 32768-65535 are reserved for use
// by customers.
//
public const uint FILE_DEVICE_BEEP = 0x00000001;
public const uint FILE_DEVICE_CD_ROM = 0x00000002;
public const uint FILE_DEVICE_CD_ROM_FILE_SYSTEM = 0x00000003;
public const uint FILE_DEVICE_CONTROLLER = 0x00000004;
public const uint FILE_DEVICE_DATALINK = 0x00000005;
public const uint FILE_DEVICE_DFS = 0x00000006;
public const uint FILE_DEVICE_DISK = 0x00000007;
public const uint FILE_DEVICE_DISK_FILE_SYSTEM = 0x00000008;
public const uint FILE_DEVICE_FILE_SYSTEM = 0x00000009;
public const uint FILE_DEVICE_INPORT_PORT = 0x0000000a;
public const uint FILE_DEVICE_KEYBOARD = 0x0000000b;
public const uint FILE_DEVICE_MAILSLOT = 0x0000000c;
public const uint FILE_DEVICE_MIDI_IN = 0x0000000d;
public const uint FILE_DEVICE_MIDI_OUT = 0x0000000e;
public const uint FILE_DEVICE_MOUSE = 0x0000000f;
public const uint FILE_DEVICE_MULTI_UNC_PROVIDER = 0x00000010;
public const uint FILE_DEVICE_NAMED_PIPE = 0x00000011;
public const uint FILE_DEVICE_NETWORK = 0x00000012;
public const uint FILE_DEVICE_NETWORK_BROWSER = 0x00000013;
public const uint FILE_DEVICE_NETWORK_FILE_SYSTEM = 0x00000014;
public const uint FILE_DEVICE_NULL = 0x00000015;
public const uint FILE_DEVICE_PARALLEL_PORT = 0x00000016;
public const uint FILE_DEVICE_PHYSICAL_NETCARD = 0x00000017;
public const uint FILE_DEVICE_PRINTER = 0x00000018;
public const uint FILE_DEVICE_SCANNER = 0x00000019;
public const uint FILE_DEVICE_SERIAL_MOUSE_PORT = 0x0000001a;
public const uint FILE_DEVICE_SERIAL_PORT = 0x0000001b;
public const uint FILE_DEVICE_SCREEN = 0x0000001c;
public const uint FILE_DEVICE_SOUND = 0x0000001d;
public const uint FILE_DEVICE_STREAMS = 0x0000001e;
public const uint FILE_DEVICE_TAPE = 0x0000001f;
public const uint FILE_DEVICE_TAPE_FILE_SYSTEM = 0x00000020;
public const uint FILE_DEVICE_TRANSPORT = 0x00000021;
public const uint FILE_DEVICE_UNKNOWN = 0x00000022;
public const uint FILE_DEVICE_VIDEO = 0x00000023;
public const uint FILE_DEVICE_VIRTUAL_DISK = 0x00000024;
public const uint FILE_DEVICE_WAVE_IN = 0x00000025;
public const uint FILE_DEVICE_WAVE_OUT = 0x00000026;
public const uint FILE_DEVICE_8042_PORT = 0x00000027;
public const uint FILE_DEVICE_NETWORK_REDIRECTOR = 0x00000028;
public const uint FILE_DEVICE_PARTITION = 0x00000029;
public const uint FILE_DEVICE_STORE = 0x00000030;

#endregion // "DeviceType"


#endregion // "CTL_CODE"
#endregion // "constants"


Filling the empty egg
A bit of additional work is required to accomplish our task, the driver class goal is to map the driver access within C#, but also an abstraction class to deeply simplify the access to the driver and then offer a servicing class for your application.

#region "private members"
private int mintHandle = INVALID_HANDLE_VALUE; // driver handle
#endregion // "private members"

#region "Constructor-Destrcutor"
public Driver()
{
}

~ Driver ()
{
if (mintHandle != INVALID_HANDLE_VALUE)
Close ();
}
#endregion // Constructor-Destrcutor

#region "Open"
public bool Open(String strDriverName)
{
if (mintHandle != INVALID_HANDLE_VALUE)
{
Close ();
}

mintHandle = CreateFile(strDriverName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);

return mintHandle != INVALID_HANDLE_VALUE;
}
#endregion // open the driver

#region "Close"
public bool Close()
{
bool bResult = false;
int iValue;
if (mintHandle != INVALID_HANDLE_VALUE)
{
iValue = CloseHandle (mintHandle);
if (iValue != 0)
{
bResult = true;
mintHandle = INVALID_HANDLE_VALUE;
}
}

return bResult;
}
#endregion // close the driver

#region "Read"
public bool Read(byte []buffer, ref int lpBytesToRead)
{
bool bResult = false;
int lpNumberOfBytesRead = 0;
int intResult = 0;
OVERLAPPED lpOverlapped = new OVERLAPPED();

if (mintHandle != INVALID_HANDLE_VALUE)
{
intResult = ReadFile(mintHandle, buffer, lpBytesToRead, ref lpNumberOfBytesRead, ref lpOverlapped);

if (intResult == 0)
{
throw new Exception("Error reading driver");
}
else
{
lpBytesToRead = lpNumberOfBytesRead;
bResult = true;
}
}

return bResult;
}
#endregion // read data from the driver

#region "Write"
public bool Write(byte []buffer, ref int lpBytesToWrite)
{
bool bResult = false;
int lpNumberOfBytesWrite = 0;
int intResult = 0;
OVERLAPPED lpOverlapped = new OVERLAPPED();

if (mintHandle != INVALID_HANDLE_VALUE)
{
intResult = WriteFile(mintHandle, buffer, lpBytesToWrite, ref lpNumberOfBytesWrite, ref lpOverlapped);

if (intResult == 0)
{
throw new Exception("Error writing driver");
}
else
{
lpBytesToWrite = lpNumberOfBytesWrite;
bResult = true;
}
}

return bResult;
}
#endregion // read data from the driver

#region "IOControl"
public bool IOControl(uint IoControlCode, byte []bufferIn, uint bufferSizeIn, byte []bufferOut, ref uint lpBufferSizeOut)
{
bool bResult = false;
uint lpNumberOfBytesReturned = 0;
int intResult = 0;
OVERLAPPED lpOverlapped = new OVERLAPPED();

if (mintHandle != INVALID_HANDLE_VALUE)
{
intResult = DeviceIoControl(mintHandle, IoControlCode, bufferIn, bufferSizeIn, bufferOut, lpBufferSizeOut, ref lpNumberOfBytesReturned, ref lpOverlapped);

if (intResult == 0)
{
throw new Exception("Error IOcontrol driver");
}
else
{
lpBufferSizeOut = lpNumberOfBytesReturned;
bResult = true;
}
}

return bResult;
}
#endregion // read data from the driver

This is it, now we have a class that really simplify the access to the drivers.


Driver myI2CDriver = new Driver();
myI2CDriver.Open("I2C1:");

myI2CDriver.Close();


- Nicolas

Tuesday, November 2, 2010

WEC 7 : Build Solution performing a Buid & Sysgen

Using the CTP version of Windows Embedded Compact 7 available from the Microsoft Connect website (http://connect.microsoft.com), you will face a unexpected behavior of the development environment.
When building a solution using the Build menu, you will launch a Build and Sysgen (blddemo.bat) command instead of a Sysgen (blddemo -q) as it was the case in the previous version. This Build and Sysgen action is rebuilding the complete content of the $(_WINCEROOT)\Public folder, that is usually useful when you are making modification into this folder. In the case of the build of a runtime image, this step is not required and is increasing the build time.
As this action is not required, it is useful to avoid the execution of a such action. This can be done by modifying the blddemo.bat file located in $(_WINCEROOT)\PUBLIC\COMMON\OAK\MISC and adding in the first lines :
If "%1"=="" goto :EOF

- Nicolas

Automaticaly Add files to an SDK - refreshed

When building an SDK using Platform Builder 6.0 (and previous), you may need to include your own files. Those files are the list of IOcontrols of drivers you implemented, or header files required to link to a new library that you want to provide in your SDK. In that case you have to include those files to your SDKs.
The first one is a manual inclusion of those files using the configuration wizard of the SDK. That’s mean that every time you generates the SDK you have to take care of those files.
The second solution is to automated this mechanism by copying those files to a specific folder that will be used automatically by the wizard during the generation of the SDK.


Identification of the sdk folder
The SDK tool uses the content of the cesysgen folder (located in your OSDesign folder), and the sdk folder located in the same folder as the cesysgen and OAK folder.$(_PROJECTROOT)\sdk or $(_PROJECTSDKROOT)
The SDK tool adds the content of the sdk\Inc and sdk\Lib folder to the SDK msi file. So you must store the files you need to provide with the SDK in those sub folders. Doing it manually is never the solution, so by asking the Windows embedded CE build environment to do it, it is more reliable. This could be done during the build of those libraries or drivers.


Add an automated task
Using the sources files you can add dependencies and enable automated task for the build engine. You could also use the postlink macro, but this is not the purpose of this article.
In the sources file add the following lines :


#------------------------------------------------------------------------------
# SDK files
#------------------------------------------------------------------------------
# Enable copy to the SDK directory
WINCETARGETFILES=$(WINCETARGETFILES) CopyFilesToSDK

# Hearder files
SDK_H_FILES=gpio_iocontrol.h driverheadertoshare.h

#Lib files
SDK_LIB_FILES=


Any target files that you specify using WINCETARGETFILES are added to the list of dependencies that must be satisfied to generate a successful build. Also by listing the files you need for your SDK in the SDK_ FILES variable, you will generate a list actions that will be solved later in the makefile.inc.


Add an automated copy instructions
In association to the SOURCES file you must provide a makefile.inc (located in the same folder), this file is proceeded after your sources files during the build. In this file you must satisfy the dependency list, so it must contain information about the CopyFilesToSDK.
Makefile.inc content will be the following :

#
# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to include new files
# in the SDK.
#

CopyFilesToSDK:
if not EXIST $(_PROJECTSDKROOT)\Inc mkdir $(_PROJECTSDKROOT)\Inc
FOR %%f IN ($(SDK_H_FILES)) DO xcopy /I /D /Q %%f $(_PROJECTSDKROOT)\Inc > nul

if not EXIST $(_PROJECTSDKROOT)\Lib mkdir $(_PROJECTSDKROOT)\Lib\$(_CPUINDPATH)
FOR %%f IN ($(SDK_LIB_FILES)) DO xcopy /I /D /Q $(_RELEASELIBDIR)\%%f $(_PROJECTSDKROOT)\Lib\$(_CPUINDPATH)\ > nul

The copy will be done in batch script language and executed by the build engine.
After the build of the driver or the library, you may find all your required files in the $(_PROJECTSDKROOT) folder.
In this article I described only one list of files to be proceeded, but it make sense to have two lists, one for the header files and the sdk\Inc folder and another one for the sdk\Lib folder for the libraries.

- Nicolas