Direct Hardware Access from the CF? Sure!

If you follow by blog, you’ve probably noticed one of my side projects is moving toward a device driver with managed code.  Why?  Because most any sane person will tell you it’s either not possible or a bad idea.  But hey, I always want to know the why behind those statements.  “It will be too slow.”  Well, how slow, exactly?  “It won’t be deterministic.”  What if I don’t need it to be, or what if I can get around that axiom?


Well as a new motivator I had someone ask about using the SPI bus on an Applied Data Systems device using C#.  It’s a slow bus, and determinism isn’t really necessary, so this would be a classic opportunity.  Add to that the fact they don’t need to to be a tru driver running under the auspices of device.exe – simply controlling the bus from their app is fine.


So last night I sat down and hammered out a general purpose physical address accessor class, which I’ll post below and add to the SDF.  Then today I did a quick implementation test with it just to see how it does.  I didn’t do the SPI driver because 1. it would take a while and 2. they need to understand how it works and it will do them some good to write it.  What I did do is use the class to control a GPIO on the PXA255 which is connected to an LED on my board.  This provided a quick visual check that I was indeed able to set the GPIO register values.  It also allowed me to use a scope to see how fast it would go.


Well, as a test I put it in a tight loop toggling the LED state as fast as possible, just a while(true) {on, off } kind of thing, and I did the same logic in a pure C app.  The C app had pulse durations ~500ns, plus or minus a pretty large margin because I didn’t fiddle with thread priorities or anything.  The C# app had pulse durations ~1us.  So you can look at it glass-half-empty and say wow, it’s twice as slow as native code, or glass-half-full and say, wow, that’s well below millisecond resolution and quite suitable for a lot of stuff.


I love managed code.


//==========================================================================================
//
// OpenNETCF.IO.PhysicalAddressPointer
// Copyright (c) 2004, OpenNETCF.org
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the OpenNETCF.org Shared Source License.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the OpenNETCF.org Shared Source License
// for more details.
//
// You should have received a copy of the OpenNETCF.org Shared Source License
// along with this library; if not, email
licensing@opennetcf.org to request a copy.
//
// If you wish to contact the OpenNETCF Advisory Board to discuss licensing, please
// email
licensing@opennetcf.org.
//
// For general enquiries, email
enquiries@opennetcf.org or visit our website at:
//
http://www.opennetcf.org
//
//==========================================================================================
using System;
using System.Runtime.InteropServices;


namespace OpenNETCF.IO
{
 /// <summary>
 /// This class is used to access memory mapped addresses
 /// !!! DANGER WILL ROBINSON !!  You can cause serious problems using this class without knowing what you’re doing!
 /// We reiterate the statement in our license that OpenNETCF provides absolutely no warranty on this code and you use it at your own risk
 /// </summary>
 public class PhysicalAddressPointer
 {
  // use 4k pages
  private const uint PAGE_SIZE  = 0x1000;


  // consts from winnt.h
  private const uint MEM_RESERVE  = 0x2000;
  private const uint PAGE_NOACCESS = 0x0001;
  private const uint PAGE_READWRITE = 0x0004;
  private const uint PAGE_NOCACHE  = 0x200;
  private const uint PAGE_PHYSICAL = 0x400;
  private const uint MEM_RELEASE  = 0x8000;


  private IntPtr m_virtualAddress = IntPtr.Zero;
  private IntPtr m_addressPointer = IntPtr.Zero;


  /// <summary>
  /// An accessor class to a physical memory address.
  /// </summary>
  /// <param name=”physicalAddress”>Physical Address to map</param>
  /// <param name=”size”>Minimum size of the desired allocation</param>
  /// <remarks>The physical address does not need to be aligned as the PhysicalAddressPointer will handle alignment
  /// The size value will aligned to the next multiple of 4k internally, so the actual allocation may be larger than the requested value</remarks>
  public PhysicalAddressPointer(uint physicalAddress, uint size)
  {
   m_addressPointer = MapPhysicalAddress(physicalAddress, size);
  }


  ~PhysicalAddressPointer()
  {
   if(m_virtualAddress != IntPtr.Zero)
   {
    VirtualFree(m_virtualAddress, 0, MEM_RELEASE);
   }
  }


  /// <summary>
  /// Write an array of bytes to the mapped physical address
  /// </summary>
  /// <param name=”bytes”>data to write</param>
  public void WriteBytes(byte[] bytes)
  {
   Marshal.Copy(bytes, 0, m_addressPointer, bytes.Length);
  }


  /// <summary>
  /// Write a 32-bit value to the mapped address
  /// </summary>
  /// <param name=”data”>data to write</param>
  public void WriteInt32(int data)
  {
   Marshal.WriteInt32(m_addressPointer, data);
  }


  /// <summary>
  /// Write a 16-bit value to the mapped address
  /// </summary>
  /// <param name=”data”>data to write</param>
  public void WriteInt16(short data)
  {
   Marshal.WriteInt16(m_addressPointer, data);
  }


  /// <summary>
  /// Write an 8-bit value to the mapped address
  /// </summary>
  /// <param name=”data”>data to write</param>
  public void WriteByte(byte data)
  {
   Marshal.WriteByte(m_addressPointer, data);
  }


  /// <summary>
  /// Read a series of bytes from the mapped address
  /// </summary>
  /// <param name=”length”>number of bytes to read</param>
  /// <returns>read data</returns>
  public byte[] ReadBytes(int length)
  {
   byte[] bytes = new byte[length];
   Marshal.Copy(m_addressPointer, bytes, 0, length);
   return bytes;
  }


  /// <summary>
  /// Read a 32-bit value from the mapped address
  /// </summary>
  /// <returns>read value</returns>
  public int ReadInt32()
  {
   return Marshal.ReadInt32(m_addressPointer);
  }


  /// <summary>
  /// Read a 16-bit value from the mapped address
  /// </summary>
  /// <returns>read value</returns>
  public short ReadInt16()
  {
   return Marshal.ReadInt16(m_addressPointer);
  }


  /// <summary>
  /// Read an 8-bit value from the mapped address
  /// </summary>
  /// <returns>read value</returns>
  public byte ReadByte()
  {
   return Marshal.ReadByte(m_addressPointer);
  }


  IntPtr MapPhysicalAddress(uint physicalAddress, uint size)
  {
   uint alignedAddress = 0;
   uint offset   = 0;
   uint alignedSize  = 0;
   IntPtr returnAddress = IntPtr.Zero;
   
   
   // get a page aligned address
   alignedAddress = PageAlignAddress(physicalAddress);
   offset = physicalAddress – alignedAddress;


   // get a page aligned size
   alignedSize = RoundSizeToNextPage(size + offset);


   // reserve some virtual memory
   m_virtualAddress = VirtualAlloc(0, alignedSize, MEM_RESERVE, PAGE_NOACCESS);


   // sanity check
   if(m_virtualAddress == IntPtr.Zero)
   {
    // allocation failure!
    return IntPtr.Zero;
   }


   
   // Map physical memory to virtual memory
   if(VirtualCopy (m_virtualAddress, (IntPtr)(alignedAddress >> 8), alignedSize,
    PAGE_READWRITE | PAGE_NOCACHE | PAGE_PHYSICAL) == 0)
   {
    // copy failure!
    VirtualFree(m_virtualAddress, 0, MEM_RELEASE);


    return IntPtr.Zero;
   }


   // offset and return
   return new IntPtr(m_virtualAddress.ToInt32() + offset);
  }


  // simply aligns an address to a page boundary to prevent data aborts and fun stuff like that
  uint PageAlignAddress(uint addressToAlign)
  {
   return addressToAlign & ~(PAGE_SIZE -1);
  }


  // allocations must be made in page multiples. 
  // this method finds the next multiple given a desired size
  uint RoundSizeToNextPage(uint size)
  {
   return (size + PAGE_SIZE – 1) & ~(PAGE_SIZE – 1);
  }


  // p/invoke declarations
  [DllImport(“coredll.dll”, EntryPoint=”VirtualAlloc”, SetLastError=true)]
  private static extern IntPtr VirtualAlloc(uint lpAddress, uint dwSize, uint flAllocationType, uint flProtect);


  [DllImport(“coredll.dll”, EntryPoint=”VirtualCopy”, SetLastError=true)]
  private static extern int VirtualCopy(IntPtr lpvDest, IntPtr lpvSrc, uint cbSize, uint fdwProtect);


  [DllImport(“coredll.dll”, EntryPoint=”VirtualFree”, SetLastError=true)]
  private static extern int VirtualFree(IntPtr lpAddress, uint dwSize, uint dwFreeType);
 }
}


 

5 thoughts on “Direct Hardware Access from the CF? Sure!”

  1. Hi, i have to send a few bytes to GPIO ports by using CF 1.0 and VB.NET. i am using a panel pc that has 8 digital ports on it and has Win CE 4.2 OS on it. I changed your code to vb.net but i can not make it work. Would you pls check the code and give me some advice? Thanx in advance
    ‘———————–
    Imports System
    Imports System.Runtime.InteropServices

    Public Class PhysicalAddressPointer
    Private Const PAGE_SIZE As Int32 = 4096
    Private Const MEM_RESERVE As Int32 = 8192
    Private Const PAGE_NOACCESS As Int32 = 1
    Private Const PAGE_READWRITE As Int32 = 4
    Private Const PAGE_NOCACHE As Int32 = 512
    Private Const PAGE_PHYSICAL As Int32 = 1024
    Private Const MEM_RELEASE As Int32 = 32768
    Private m_virtualAddress As IntPtr = IntPtr.Zero
    Private m_addressPointer As IntPtr = IntPtr.Zero

    Public Sub New(ByVal physicalAddress As Int32, ByVal size As Int32)
    m_addressPointer = MapPhysicalAddress(physicalAddress, size)
    End Sub

    Protected Overrides Sub Finalize()
    If Not IntPtr.op_Equality(m_virtualAddress, IntPtr.Zero) Then
    VirtualFree(m_virtualAddress, 0, MEM_RELEASE)
    End If
    End Sub

    Public Sub WriteBytes(ByVal bytes As Byte())
    Marshal.Copy(bytes, 0, m_addressPointer, bytes.Length)
    End Sub

    Public Sub WriteInt32(ByVal data As Integer)
    Marshal.WriteInt32(m_addressPointer, data)
    End Sub

    Public Sub WriteInt16(ByVal data As Short)
    Marshal.WriteInt16(m_addressPointer, data)
    End Sub

    Public Sub WriteByte(ByVal data As Byte)
    Marshal.WriteByte(m_addressPointer, data)
    End Sub

    Public Function ReadBytes(ByVal length As Integer) As Byte()
    Dim bytes(length) As Byte
    Marshal.Copy(m_addressPointer, bytes, 0, length)
    Return bytes
    End Function

    Public Function ReadInt32() As Integer
    Return Marshal.ReadInt32(m_addressPointer)
    End Function

    Public Function ReadInt16() As Short
    Return Marshal.ReadInt16(m_addressPointer)
    End Function

    Public Function ReadByte() As Byte
    Return Marshal.ReadByte(m_addressPointer)
    End Function

    Function MapPhysicalAddress(ByVal physicalAddress As Int32, ByVal size As Int32) As IntPtr
    Dim alignedAddress As Int32 = 0
    Dim offset As Int32 = 0
    Dim alignedSize As Int32 = 0
    Dim returnAddress As IntPtr = IntPtr.Zero
    alignedAddress = PageAlignAddress(physicalAddress)
    offset = physicalAddress – alignedAddress
    alignedSize = RoundSizeToNextPage(size + offset)
    m_virtualAddress = VirtualAlloc(0, alignedSize, MEM_RESERVE, PAGE_NOACCESS)
    If IntPtr.op_Equality(m_virtualAddress, IntPtr.Zero) Then
    Return IntPtr.Zero
    End If

    If VirtualCopy(m_virtualAddress, CType((alignedAddress > 8), Int32), alignedSize, PAGE_READWRITE Or PAGE_NOCACHE Or PAGE_PHYSICAL) = 0 Then
    VirtualFree(m_virtualAddress, 0, MEM_RELEASE)
    Return IntPtr.Zero
    End If
    Return New IntPtr(m_virtualAddress.ToInt32 + offset)
    End Function

    Function PageAlignAddress(ByVal addressToAlign As Int32) As Int32
    Return addressToAlign And Not (PAGE_SIZE – 1)
    End Function

    Function RoundSizeToNextPage(ByVal size As Int32) As Int32
    Return (size + PAGE_SIZE – 1) And Not (PAGE_SIZE – 1)
    End Function

    Declare Function VirtualAlloc Lib "Coredll.dll" Alias "VirtualAlloc" (ByRef lpAddress As Int32, ByVal dwSize As Int32, ByVal flAllocationType As Int32, ByVal flProtect As Int32) As IntPtr

    Declare Function VirtualCopy Lib "Coredll.dll" Alias "VirtualCopy" (ByVal lpvDest As IntPtr, ByVal lpvSrc As Int32, ByVal cbSize As Int32, ByVal fdwProtect As Int32) As Integer

    Public Declare Function VirtualFree Lib "Coredll.dll" Alias "VirtualFree" (ByRef lpAddress As IntPtr, ByVal dwSize As Int32, ByVal dwFreeType As Int32) As Int32

    End Class
    ‘———————–
    And here is my way to use this class
    ‘———————–
    Dim myObject As PhysicalAddressPointer
    Try
    myObject.MapPhysicalAddress(864, 1)
    myObject.WriteByte(&HC1)
    Catch ex As Exception
    MsgBox(ex.Message)
    End Try

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s