Friday, February 10, 2012

Learning to hate AllocateAdapterChannel

Copyright © 2012, Steven E. Houchin. All rights reserved.

I have a scatter-gather device I'm supporting in a Windows PNP driver. Normally, a driver developer would use the GetScatterGatherList API to map multiple IRP buffers to a device's DMA capabilities. However, in my case, my device has peculiar buffer alignment requirements that I can't count on GetScatterGatherList to handle. But, no worries. The AllocateAdapterChannel API is available.

Wait! Not so fast. In order for my device to perform at peak speed, multiple Read and Write IRPs are simultaneously active and mapped to the DMA. What this means is, I can't wait for the occurrence of an interrupt and an IRP completion before mapping the next IRP to the DMA; I map dozens or hundreds of them to the DMA in advance.

GetScatterGatherList handles all this just fine as long as each IRP has its own associated separate DMA_ADAPTER object. In theory, AllocateAdapterChannel should be able to do the same, but it can't. There is this sneaky little note in the WDDK documentation that throws a fly into the oatmeal:

Only one DMA request can be queued for a device object at any one time. Therefore, the driver should not call AllocateAdapterChannel again for another DMA operation on the same device object until the AdapterControl routine has completed execution.

The key phrase there is "device object." DEVICE_OBJECT is a parameter to AllocateAdapterChannel. So, even if I have a separate DMA_ADAPTER object for each mapped IRP, it still uses just the one DEVICE_OBJECT. The note above blandly states "until the AdapterControl routine has completed execution." Exactly how do I know when it completes execution? If the driver is executing its assembly "ret" instruction, it is technically still in the AdapterControl callback, so anything I do inside it to notify another thread to proceed is too early.

I believe the issue here is that DEVICE_OBJECT is placed on a wait list by the kernel when the DMA is not immediately available. Thus, we can't have that object placed twice on a list. Maybe the answer is to create a separate, fake DEVICE_OBJECT for each IRP, just like I do with DMA_ADAPTER.

Charging ahead, I created a pool of DEVICE_OBJECTs, copied from the original, and used them round-robin for each simultaneous AllocateAdapterChannel, preventing any more calls if none available.

No luck. The failure manifests itself by the AdapterControl being called back twice in a row for the same IRP - i.e. two calls to AdapterControl for an IRPs single call to AllocateAdapterChannel. Now, maybe I am still doing something wrong managing my fake DEVICE_OBJECTs. For example, when do I really know that a DEVICE_OBJECT is available for reuse? It's the fly and oatmeal problem again with the AdapterControl callback: when has it really "completed execution?"

For now, I don't have a workable solution to this. I'll let you know if I figure it out.

Monday, October 24, 2011

Computing the viewport size within a WPF Border control

Copyright © 2011, Steven E. Houchin. All rights reserved.

I have a WPF application that renders a photo bitmap inside a System.Windows.Controls.Image control that is contained within a System.Windows.Controls.Border. If the photo bitmap is too large for the app window (either in the horizontal or vertical direction), I set the Image's corresponding size attribute(s) to 'Auto' (i.e. Double.NaN) within the Image's SizeChanged event handler. If the bitmap is smaller than the app window, I set the Image's size to the actual fixed size of the bitmap, so it doesn't get scaled.

But, that left a problem when I resized the app window smaller: a fixed-size Image was truncated by the border when the window shrank smaller than the photo's bitmap size. And, because the Image's width/height weren't set to Auto, its SizeChanged event wasn't firing.

However, the Border's SizeChanged event was firing. So, I added that handler for the Border control, planning to set the Image's proper width/height after resize.  That left another problem in this new event handler: how to calculate whether or not the Image control's width/height should be Auto or fixed.  Put another way, what was the size of the viewport inside the Border control where the Image would be rendered?

The Border's SizeChanged event handler is passed a SizeChangedEventArgs parameter that provides the new width/height of the Border control. The size available to the Image control inside that will clearly be smaller, but by how much? Here is the algorithm in C# that I came up with (keep in mind that a negative BorderThickness means the border is drawn outside the control's drawing area):

// Calculate the Border control's new viewport
// size within the drawn border where the Image control
// is rendered (add in any padding and non-negative
// border thickness).
Size viewport = new Size();
viewport.Width = e.NewSize.Width -
    (border.Padding.Left + border.Padding.Right) -
    ((border.BorderThickness.Left > 0) ?
        border.BorderThickness.Left : 0) -
    ((border.BorderThickness.Right > 0) ?
        border.BorderThickness.Right : 0);
viewport.Height = e.NewSize.Height -
    (border.Padding.Top + border.Padding.Bottom) -
    ((border.BorderThickness.Top > 0) ?
        border.BorderThickness.Top : 0) -
    ((border.BorderThickness.Bottom > 0) ?
        border.BorderThickness.Bottom : 0);
This viewport variable now holds the size available for the Image control inside the border. Now, all I have to do is determine if the Image needs to be scaled down in the viewport or set to a fixed size. One thing I discovered is that the Image's Margin value must be taken into account, since it is also rendered inside the viewport along with the photo bitmap. The code to determine the right sizing, in this case for the width is:

// Calculate the image's real width to determine if
// scaling is needed
double imageWidth = _bitmapActualSize.Width +
    image.Margin.Left + image.Margin.Right;
if (viewport.Width > imageWidth)
{
    // Restrict the Image control's width to the
    // bitmap's actual width
    image.Width = _bitmapActualSize.Width;
}
else if (viewport.Width < imageWidth)
{
    if (Double.NaN != image.Width)
    {
        // Set the Image control's width to 'Auto' so
        // it will scale down to fit
        image.Width = Double.NaN;
    }
}
The same calculation is done for the Image height immediately after. This results in the proper change to the photo bitmap size just at the right time.

Wednesday, August 31, 2011

Eliminating Visible Image Borders on Blogger

Copyright © 2011, Steven E. Houchin

Something that has driven me crazy for quite awhile about Blogger is its propensity to place a thick white border around any image I added to my postings. Editing the HTML of the img element to include border="0" or a style="border: none;" or anything else like that was simply ignored. What the heck was happening?

Then, I found a hint on another site, where it referred to Blogger's "template style sheet." After some poking around in Blogger, I stumbled across it. When logged in, navigate to the Dashboard page, then select the Design tab (or link), then click the Edit HTML link along the top. This brings up a screen that allows editing of your blog template, which contains the HTML, CSS, and Javascript that is the basis for every posting. On this template page, it suggests you download your existing template before messing with it, which sounds like good advice. Under the Edit Template heading is the text of the template which you can alter.

Okay. Now to the nuts and bolts of what I did to eliminate the image border. In my template, there was a CSS directive like this:

.post-body img,  .post-body .tr-caption-container { 
   padding: 8px;
}

The 8 pixels of padding is the border I saw, because it seems to inherit a white background color from elsewhere. My images are controlled by the ".post-body img" class. So, I deleted that class specification from the above and created a new one that had the padding and background specifications I wanted:

.post-body .tr-caption-container {
  padding: 8px;
}

.post-body img {
  padding: 8px;
  background: $(post.background.color);
}

This keeps the padding (which I like since it provides some separation between the image and the text) and makes the background color for the image's box the same as the post's color. The $(post.background.color) value is, I think, a variable handled by Blogger's XSL processing that is replaced in the final output with the actual color (or "transparent" in my case).

Once I saved these CSS changes to my template ... voila! The image borders in all my posts (new and old) vanished. In reality, all I really did was change the background color of the padding for just the images.

Thursday, August 11, 2011

Finding IWin32Window in WPF

Copyright © 2011, Steven E. Houchin

I'm developing a WPF desktop application (C# and .NET 3.5) that needs to pop up a dialog window allowing the user to browse for a folder.  .NET provides a convenient (and bland) dialog that does this: System.Windows.Forms.FolderBrowserDialog. To pop up this dialog as modal from my app, I must call its ShowDialog method. Since I want it to be a child of my main window, I need to pass it the parent window, which should be straightforward given the "owner" parameter to the method in question:

DialogResult ShowDialog(System.Windows.Forms.IWin32Window owner);

The problem is, the would-be parent window of that dialog is of type System.Window, not IWin32Window, so "this.Owner" won't match the datatype of ShowDialog's "owner" parameter.  So, given that I have a WPF System.Window-derived parent class, where do I obtain an IWin32Window object?

Well, it turns out I have to write a tiny bit of code for it. Specifically, I must implement the IWin32Window interface on the parent window's class:

public partial class Main : Window,
    System.Windows.Forms.IWin32Window
{
...
   #region IWin32Window implementation
   IntPtr System.Windows.Forms.IWin32Window.Handle
   {
      get
      {
         return ((HwndSource)
             PresentationSource.FromVisual(this)).Handle;
      }
   }
   #endregion
...
}

With the IWin32Window.Handle property now implemented in the parent class, I can simply call ShowDialog(this). Note that you must also include a "using System.Windows.Interop" for this to work.


Wednesday, September 22, 2010

Kernel Driver Signing - Part III

Copyright © 2010, Steven E. Houchin

In Parts I and II of this article, I discussed the requirement for driver signing, digital signatures, and how to go about testing signed drivers.

The last step is the actual signing of the binaries that you integrate into the customer product. In order to do this, you must first purchase a digital certificate from a reputable certificate authority (CA). In fact, for kernel mode drivers, you must purchase your digital certificate from Verisign. Certificates from other vendors can be used to sign the binaries, and Windows installs them, but it refuses to execute those binaries (as of Windows 7).

As I've mentioned in the earlier parts of this article, the step-by-step procedures for driver signing are contained in a document at the Windows WHDC site, and need not be repeated here. It contains a section titled "The Kernel-Mode Code-Signing Process." The important decision to make is whether to set up the digital signature via a catalog file (specified in the INF file), or to sign the binaries directly. A cartalog is preferable, especially if you are signing multiple files; but that won't work if your binaries start at boot time (see StartType in your INF fle). In that case, signing the binarys themselves is required. Microsoft has another site, titled "Signing Drivers for Public Release", that gives more details about the files you may receive from your CA.

When you distribute your product for the customer to install, the installer package must include, in addition to the binaries, the INF file, signed catalog files (if any), and the public key portion of your digital certificate. The public key digital certificate (.spc or .cer file) must be installed on your customer's system. Your original private key digital certificate (.pvk file) should be locked away, because it is used in the signing process.

Monday, August 9, 2010

Kernel Driver Signing - Part II

Copyright © 2010, Steven E. Houchin

In Part I of this article, I explained that all kernel drivers on 64-bit Windows systems must be digitally signed.

There are two ways of signing kernel drivers: embed the signature in the driver binaries themselves, or use a signed catalog file. To peform driver signing, you really must have the latest Microsoft WDK installed on your signing system.

For drivers that start at boot time (see StartType in your INF fle), the binary itself must be signed.  Use of a catalog file is not enough, though is also required if installing the driver package via INF file.  For other types of drivers - especially those that contain multiple files - a signed catalog file can be used to identify all the files in a driver package. 

Use of a catalog file requires that the driver package be installed via an INF file, because that is where the catalog file is specified. For example, ADriver.inf will have the following line included in its [Version] section:
CatalogFile=ADriver.cat
After your driver binaries are all built and copied to a "package" directory (which includes the INF file), a catalog file is created using the WDK's Inf2Cat utility. For example, if your package files are in C:\MyDriverPackage, the catalog file (in this case for Win2K and Win64 systems) is created via:
Inf2Cat /driver:C:\MyDriverPackage\ /os:2000,Server2003_X64
This utility will scan all INF files in the given directory and create catalog files for each one using the "CatalogFile" line.

Once the catalog files are created, they must be signed using WDK's SignTool utility and your digital certificate information.  The details for catalog file creation and signing, and driver binary signing, are at the Windows WHDC site, which contains white paper that has decent step-by-step procedures.

The catalog file and INF file go together as a pair wherever the signed driver package is to be installed.  If your INF file is also used for 32-bit drivers, then your 32-bit installer should comment out the "CatalogFile" line in the [Version] section so that a signing certificate isn't needed on those systems.

Tuesday, July 27, 2010

Kernel Driver Signing - Part I

Copyright © 2010, Steven E. Houchin


Back in January of 2006, Microsoft announced - much to developers' dismay - that all kernel drivers on 64-bit Windows systems must be signed. Signing is defined as:
the process of assigning an encrypted digital signature to executables in order to confirm the software's author and to guarantee that the code has not been altered or corrupted
Once I ported my client's driver code to 64-bit, I found myself in an "uh-oh" situation: how do I test the changes on my development systems without having my client's digital signing certificate?  A digital certificate is:
a digital signature from a certificate authority (CA) that contains a private key used to sign some software, which must match the public key in the CA's certificate installed on a user's system
The private key (PK) is necessary for me to sign the driver executables, and is something that the software vendor (my client) must not pass around to anyone else - including me - lest it fall into the wrong hands and be used for malicious signing. That gets us back to my dilemma: how do I test without the PK?

The answer provided by MSFT is Test Signing. The Windows WHDC site contains a good white paper on this subject that has decent step-by-step procedures. The basic idea is this: a developer can create a Test Certificate, which is used on the development systems to sign and validate executables.  The catch is, the executables will only work if the target test system is configured to boot into a special Test Signing mode. This is done on the target system with the BCDEDIT command. Note that, in the past, it was also possible to boot Windows into a "Disable Driver Signature Enforcement" mode, where signatures were ignored. That option, while still visible via F8 boot options, does not actually work anymore.

Next time: Test Signing using catalog files.