관리 메뉴

kisoo

NDIS FOR WINCE 본문

01.About Programming /5.Mobile Lab

NDIS FOR WINCE

JamesK78 2010. 1. 12. 16:17

Reprinted from PC/104 Embedded Solutions / Winter 2000 / 1
It hasn’t taken long for Microsoft’s® Windows CE® (WinCE)
OS to become a key player in the embedded OS market. The
speed at which the OS has risen in popularity has left both
network chip developers and systems integrators scrambling to
produce software drivers for their products. Although Microsoft
touts the NDIS interface in WinCE as a way to provide an
OS-independent API for network device drivers, developing any
driver for WinCE is certainly non-trivial. WinCE currently supports
only NDIS Ethernet and IrDA miniport drivers, but several factors
make writing even these simple NDIS drivers for WinCE somewhat
complicated.
First of all, WinCE NDIS device drivers follow a different model
from the traditional stream interface drivers that are most typical
for Windows CE. NDIS device drivers are loaded differently and
handle interrupt requests differently from other types of device drivers.
More importantly, the WinCE version of NDIS lacks some of
the functions needed for certain types of network drivers, particularly
those utilizing Direct Memory Access (DMA). The process by which
WinCE NDIS device drivers are built and installed present another
complication.
This article discusses some of the issues a developer of WinCE NDIS
device drivers can expect to encounter. We will start with a basic
overview of the WinCE Device Manager, focusing on driver loading,
WinCE interrupt handling, and WinCE memory management.
In each case we will start by covering the topic in general, and then
discuss specifics for NDIS device drivers. Next, we’ll talk about how
your driver will get built and installed onto the target device. Finally,
we’ll conclude with a simple discussion of exactly what steps are
required to port an existing NDIS device driver from another OS.
This article alone won’t give you quite enough information to write
a WinCE driver if you’ve never been exposed to NDIS before. If
this is the case, I would suggest that immediately after reading this
article, you refer to the “Network Drivers” section of the Windows
2000 DDK documentation. This article should provide enough context
so that on reading that documentation, you will understand how
the information relates to WinCE.
Just to warn you in advance — we won’t be discussing WinCE 3.0
specifics, NDIS drivers outside the scope of NDIS miniports, power
management, or NT Embedded. Some of the alternate information
sources presented at the end of the article discuss these topics if you
are interested.
WinCE NDIS Device Driver Basics
This section discusses how WinCE NDIS device drivers are loaded
and how they handle interrupts. In each case, we’ll look at WinCE
device drivers in general as well as the specific case of WinCE NDIS
drivers.
How Device Drivers Get Loaded
Figure 1 shows the WinCE Device Driver hierarchy and which
drivers are loaded by whom. At the top, we have the device drivers
GWES.EXE (Graphical Windowing and Event Subsystem)
and DEVICE.EXE (also known as the Device Manager). These
two drivers are loaded very early in the boot process by the
kernel. Native drivers whose operation is integral to the function
of the OS are loaded by GWES.EXE and DEVICE.EXE. Stream
Interface Drivers for devices such as serial ports are typically
loaded by DEVICE.EXE in one of two ways. Drivers for
built-in devices are loaded if they are included in the
HKEY_LOCAL_MACHINE\Drivers\Builtin registry key. On the
other hand, PC Card device drivers can be loaded using registry
entries based on either their device Plug-and-Play identifier or
with a separate detection function; this will be discussed in more
detail below.
NDIS device drivers are loaded by a parent device driver contained
in the NDIS.DLL dynamically linked library, or DLL. I will
refer to NDIS.DLL from now on as the NDIS “Wrapper” to distinguish
it from the NDIS Miniport drivers that are the focus of
this article. The NDIS Wrapper is part of the Run-Time Library
provided by Microsoft, and is actually a stream interface driver.
This driver can be loaded either as a built-in device or as a
PC-Card add-on device as discussed above.
When loaded as a “built-in device”, the NDIS Wrapper scans the
HKEY_LOCAL_MACHINE\COMM registry key for NDIS drivers,
loading each in turn. Unlike NDIS drivers under other operating
systems, WinCE NDIS drivers are always loaded as a DLL. As
with all DLLs, they must export an entry function (usually called
DLLEntry) that is called by the OS when the DLL file is loaded
or unloaded. DLLEntry for NDIS drivers typically only prints out
debug messages that give some indication that the driver was, in fact,
loaded.
Figure 1
By Michael Migdol
PCNTN4M.DLL NE2000.DLL
NDIS.DLL Ser, Aud, FLD
KB, TP, DISP PCMCIA.DLL
NDIS Drivers
StreamXface Drivers
Native Drivers
GWES.EXE DEVICE.EXE
Device Protection
Device Driver Hierarchy
System
Boot
or
Application
Specific
2 / Reprinted from PC/104 Embedded Solutions / Winter 2000
After the driver DLL has been loaded, the NDIS Wrapper calls the
driver’s initialization function called DriverEntry. The NDIS wrapper
makes calls to the functions provided by the driver through a
function table. DriverEntry normally sets up the NDIS data structure
that contains this information, and then calls NdisMRegisterMiniport
to register this information with the NDIS Wrapper. Figure 2 illustrates
this relationship.
Drivers for PCMCIA cards are slightly more complex. A native
driver, PCMCIA.DLL, provides the interface to the socket itself.
The Device Manager (DEVICE.EXE) loads PCMCIA.DLL and
then requests notification whenever a new PCMCIA device is
inserted. When this occurs, the Device Manager then queries
the PCMCIA device driver to find the card’s device identifier
(also known as the logical device ID) from the card’s PCMCIA
attribute space. This identifier is then compared against the subkeys
of the registry key HKEY_LOCAL_MACHINE\Drivers\
PCMCIA for a match, loading the driver in that sub-key if it finds
one. For network devices, the driver loaded will be the NDIS wrapper,
NDIS.DLL.
If no match is found, the detection functions listed in the
HKEY_LOCAL_MACHINE\Drivers\PCMCIA\Detect registry
key are called. If one of these detection functions returns a positive
response, the driver indicated in that registry key will be loaded.
Again, this will be NDIS.DLL for network devices.
The detection functions are handy if you have a network device
that uses a common programming interface such as NE2000
or LANCE. In these cases, devices with many different device
identifiers will be able to use the same device driver. We’ll
talk more about how registry keys can be added to the OS a
little later. Details on the contents of the registry are beyond
the scope of this paper, but can be found in the WinCE DDK
documentation.
How Interrupts are Handled
Interrupts under WinCE follow a complex path. Typically, hardware
devices assert an interrupt signal input of a programmable interrupt
controller. The interrupt controller generates a vector that is used as
an index into the operating system’s “Interrupt Vector Table.”
In WinCE, the OS function HookInterrupt is used to assign an
Interrupt Service Routine (ISR) to any given interrupt vector. ISRs
are run at kernel level in WinCE; and within the ISR, interrupts are
disabled so that nested interrupts cannot occur.
ISRs have the option of completely handling the interrupt, or returning
a system interrupt level. In the latter case, an event is generated,
notifying any device drivers that had previously requested notification
for that interrupt level using the InterruptInitialize OS call. The driver
can then process the interrupt as a user-level thread (called an Interrupt
Service Thread or IST).
During execution of the OEM-specific platform initialization function
(OEMInit), two critical steps occur to set up interrupts. First
of all, the programmable interrupt controller is configured to generate
the appropriate interrupt vector for each of its hardware interrupt
inputs. Next, all interrupts are routed through a platform-specific
interrupt dispatcher during execution of the platform’s OEMInit
function. To do this, HookInterrupt is called once for each interrupt
vector. The common platform dispatcher, ISR, returns the system
interrupt level associated which the detected hardware interrupt.
Although it is important to understand how interrupts are handled in
WinCE, you shouldn’t have to use HookInterrupt or
InterruptInitialize, since NDIS device drivers go through the NDIS
API for setting up interrupt handlers. The NDIS data structure set
up by the prior call to NdisMRegisterMiniport includes pointers
to two interrupt-related functions provided by the driver:
HandlerInterruptHandler and ISRHandler.
Early in its initialization, the NDIS driver calls NdisMRegister-
Interrupt, at which time the NDIS Wrapper creates a new IST that
calls InterruptInitialize and then goes to sleep waiting for an interrupt.
Upon awakening, the IST will call the handler pointed to by the
ISRHandler field of the data structure. Despite its name, ISRHandler
is not executed at interrupt level.
The ISRHandler function has the following prototype:
VOID ISRHandler(
OUT PBOOLEAN InterruptRecognized,
OUT PBOOLEAN QueueDpc,
IN PVOID Context
)
Usually, ISRHandler only disables further interrupts from the network
adapter and returns values of TRUE for InterruptRecognized and
QueueDpc. InterruptRecognized can be used to handle shared interrupts,
in which case a FALSE value would indicate that the generated
interrupt needs to be handled by another driver. QueueDPC will
result in a “delayed procedure call” to HandlerInterruptHandler by
the NDIS Wrapper.
There are a couple of caveats to interrupt handling for WinCE NDIS
device drivers. First, take a peek at your platform’s OEMInit function
to make sure that the hardware interrupt which your device will
use is associated with a system interrupt vector in the platform interrupt
dispatcher. The other thing to watch out for is that some early
versions of WinCE required that the desired interrupt for the network
controller be mapped to the system interrupt level indicated by
the SYSINTR_NETWORK macro (which was then located in the
NKINTR.H file). This was because the NDIS wrapper would use
SYSINTR_NETWORK when it called InterruptInitialize, regardless
of what interrupt level was passed to NdisMRegisterInterrupt.
Figure 3 summarizes the chain of events in the processing of a typical
NDIS device driver interrupt.
Memory Management for
Win CE NDIS Device Drivers
So now we know how WinCE NDIS device drivers are loaded and
how they handle interrupts. The next thing to understand is how
memory is managed in WinCE.
MiniportInitialize
MiniportSend
MiniportISR
MiniportInitialize
MiniportSend
MiniportISR
DriverEntry
pDriverInfo
NdisMRegisterMiniport
(pDriverInfo)
NDIS.DLL
MiniportInitialize
MiniportSend
MiniportISR
MINIPORT.DLL
Looking more closely at
NdisMRegisterMiniport
Figure 2
Reprinted from PC/104 Embedded Solutions / Winter 2000 / 3
Regardless of the platform, WinCE runs under a flat, 32-bit address
space. Applications run in private 32 Mbyte regions, implemented
using virtual addressing. The OS “user address space” is the bottom
2 Gbyte (8000 0000h); physical memory is mapped starting at the
top of this address space. Win32 API calls all use virtual address
which exist in the user address space.
DMA and WinCE
Device drivers often use Direct Memory Access (DMA) to transfer
data to and from the device. In DMA, some agent besides
the CPU moves data. This agent might reside in the device itself
(Master DMA), or it might be a dedicated DMA controller (Slave
DMA). Regardless of which type of DMA is being used, the DMA
agent must know the physical address of the data being transferred.
It doesn’t know about the virtual addresses used in the
Win32 API.
In the Slave DMA case, it will be necessary to program the DMA
controller directly from the device driver. The NDIS Wrapper for
other OSs typically provides a layer of abstraction so that this is not
necessary, but this feature is not present in WinCE. There is a good
example of how the DMA controller programming is accomplished
in the NSC IrDA miniport example driver in the WinCE DDK.
In both kinds of DMA, device drivers typically need to be able to
do one of two things to handle the translation from virtual memory
to physical memory. One is to inquire the OS about the physical
addresses of each “chunk” of a buffer’s data, since the buffer may
not be contiguous in physical memory. In this case, it is also necessary
to lock the memory so that the physical address does not
change after the address has been determined. Depending on the
DMA engine architecture, it may be possible to program several
physical buffer addresses at one time. The other option to this “scatter-
gather” approach is to allocate a buffer of memory that is specially
flagged to be contiguous in physical memory.
Although NDIS API provides functions to address both of these scenarios,
this subset of NDIS functionality is yet again not present in
WinCE. Specific examples of these omissions include:
Comments