Figure 1   Configuration Manager Services

Service (CM_Xxx)

Description

Add_Empty_Log_Conf

Creates an empty logical configuration for a given DEVNODE. First step in defining resource requirements for a device.

Add_ID

Used during CONFIG_SETUP to add a hardware ID or compatible ID to a DEVNODE.

Add_Range

Expand a memory range list by adding one more range description.

Add_Res_Des

Add a resource descriptor to a growing logical configuration.

Call_At_Appy_Time

Arrange an application time callback.

Call_Enumerator_Function

Call the enumerator for a given DEVNODE.

CallBack_Device_Driver

Call a device driver once for each device it manages, passing CONFIG_CALLBACK as the function code.

CallBack_Enumerator

Call an enumerator function once for each device it enumerates, passing CONFIG_CALLBACK as the function code. This makes calls for each bus or controller rather than for each attached device.

Create_DevNode

Create a new DEVNODE. This is the basic function for enumerators to use in expanding the hardware tree.

Create_Range_List

Create an empty memory range list.

Delete_Range

Remove a single memory range description from a range list.

Deregister_Arbitrator

Deregister a resource arbitrator.

Disable_DevNode

Disable a device. That is, stop using it.

Dump_Range_List

Dump a range list for debugging.

Dup_Range_List

Create a copy of a range list.

Enable_DevNode

Reverse the effect of a Disable_DevNode call.

Fail_Change_Config

Used by the Plug and Play BIOS enumerator to cancel a previously planned docking or undocking.

First_Range

Retrieve the first range element in a range list.

Free_Log_Conf

Release the memory used by a logical configuration.

Free_Range_List

Release the memory used by a memory range list.

Free_Res_Des

Release the memory used by a single resource descriptor.

Get_Alloc_Log_Conf

Get the resources actually allocated for a device. This is the main service used by a device driver to learn what resources it may use.

Get_Bus_Info

Retrieve the bus information installed by Set_Bus_Info.

Get_Child

Get a pointer to the first child of a given DEVNODE.

Get_CRC_CheckSum

Calculate a cyclic redundancy check for a given byte string.

Get_DDBs

Locate the device descriptor block(s) for drivers associated with a DEVNODE. This allows the caller to do Directed_Sys_Control calls to those drivers.

Get_Depth

Determine how deep a DEVNODE is in the hardware tree.

Get_Device_Driver_Private_DWord

Retrieve the private value stored in the DEVNODE by Set_Device_Driver_Private_Dword.

Get_Device_ID

Retrieve the ASCII device ID for a DEVNODE.

Get_Device_ID_Size

Get the byte length of the device ID. This allows the caller to allocate memory for a call to Get_Device_ID.

Get_DevNode_Key

Retrieve the ASCII name of the hardware or software registry key for a DEVNODE.

Get_DevNode_Key_Size

Get the byte length of a registry pathname. This allows the caller to allocate memory for a call to Get_DevNode_Key.

Get_DevNode_Status

Retrieve the current status of a DEVNODE.

Get_First_Log_Conf

Get a handle to the first logical configuration for a DEVNODE. This is how the caller starts a loop over configurations.

Get_Hardware_Profile_Info

Retrieve information about a hardware profile.

Get_HW_Prof_Flags

Get configuration-specific flags for a single DEVNODE in a particular hardware profile.

Get_Next_Log_Conf

Get a handle for the next logical configuration in a list of configurations.

Get_Next_Res_Des

Get a handle for the first or next resource descriptor in a logical configuration.

Get_Parent

Get a pointer to the parent of a DEVNODE.

Get_Performance_Info

Retrieve performance information for debugging purposes.

Get_Private_DWord

Retrieve the private DWORD that a driver stored about one DEVNODE in some other DEVNODE.

Get_Res_Des_Data

Copy a resource descriptor given its handle. Used only by Ring 3 callers.

Get_Res_Des_Data_Size

Get the byte length of resource data. This allows a Ring 3 caller to allocate memory for a call to Get_Res_Des_Data.

Get_Sibling

Get the next sibling of a DEVNODE. Used during a loop over children of a given node.

Get_Version

Get the version number (that is, 0x0400) of the CONFIGMG device driver.

Intersect_Range_List

Create the intersection of two range lists.

Invert_Range_List

Create the inversion of a range list.

ISAPNP_To_CM

Convert a device's resource requirements from the format specified in the ISA Plug and Play specification to Configuration Manager's internal resource descriptor format.

Load_DLVxDs

Dynamically load one or more VxDs.

Locate_DevNode

Find a DEVNODE given its ASCII ID.

Merge_Range_List

Create the union of two range lists.

Modify_Res_Des

Modify a resource descriptor. Used only by Ring 3 callers.

Move_DevNode

Used by Device Installer to eliminate duplicate DEVNODEs.

Next_Range

Get the next range descriptor in a range list.

Query_Arbitrator_Free_Data

Determine which resources managed by a resource arbitrator are free.

Query_Arbitrator_Free_Size

Determine the byte length of an arbitrator's free data structure. This allows the caller to allocate memory for a call to Query_Abitrator_Free_Data.

Query_Change_Config

Determine if it's okay to change from one hardware profile to another.

Query_Remove_SubTree

Determine if it's okay to remove a DEVNODE and all of its children.

Read_Registry_Log_Confs

Convert a logical configuration in the registry into Configuration Manager's internal form. Used by the Root Enumerator to configure legacy devices.

Read_Registry_Value

Read a value from the registry. Used by all sorts of Plug and Play drivers to obtain information beyond normal configuration data.

Recompute_HW_Prof

Used by the Plug and Play BIOS enumerator after a docking station change.

Reenumerate_DevNode

Force Configuration Manager to enumerate a DEVNODE. Used, for example, when a bus or controller detects a hot hardware change.

Register_Arbitrator

Register a function as a resource arbitrator.

Register_Device_Driver

Register a device driver for a DEVNODE. Every device driver makes this call.

Register_DevLoader

Indicates that a static VxD that earlier received a PNP_New_DevNode (DEVLOADER) message and returned CR_DEVLOADER_NOT_READY is now ready to be a Device Loader and wants to get the message a second time. Few device loaders make this call.

Register_Enumerator

Register an enumeration function. Every bus or controller enumerator makes this call.

Remove_SubTree

Removes a DEVNODE and its children from the hardware tree. This is part of removing a device from the system.

Remove_Unmarked_Children

Removes DEVNODEs whose preservation marks were cleared by Reset_Children_Marks.

Reset_Children_Marks

Clears the preservation mark for the children of a given DEVNODE.

Run_Detection

Spawns the (Ring 3) detection DLL following insertion of a legacy card.

Set_Bus_Info

Record information about a hardware bus represented by a given DEVNODE.

Set_Device_Driver_Private_DWord

Set the private value in a DEVNODE.

Set_HW_Prof

Used by Control Panel to change the current hardware profile.

Set_HW_Prof_Flags

Set the profile-specific flags for a DEVNODE.

Set_Private_DWord

Record a private value about one DEVNODE in some other DEVNODE.

Set_Private_Problem

Record a "problem" in a DEVNODE so the class installer can diagnose it for the user.

Setup_DevNode

Send a CONFIG_SETUP or CONFIG_READY to a DEVNODE's enumerator.

Sort_NodeList

Used by a resource arbitrator to sort a list of logical configurations according to how easy their requirements will be to satisfy.

Test_Range_Available

Determine if a memory range is available given reservations recorded in a range list.

Write_Registry_Value

Set a value in the registry.

Yield

Yield control until a time-out expires. Used to satisfy hardware timing requirements.

Figure 2   name

Priority

Description

LCPRI_FORCECONFIG

Configuration manually selected by the user

LCPRI_BOOTCONFIG

Configuration established at boot-time by BIOS or card

LCPRI_DESIRED

Preferred configuration giving better performance

LCPRI_NORMAL

Configuration giving acceptable performance

LCPRI_SUBOPTIMAL

Workable configuration giving poor performance

LCPRI_RESTART

Configuration will require restarting Windows

LCPRI_REBOOT

Configuration will require soft reboot of computer

LCPRI_POWEROFF

Configuration will require hard reboot of computer

LPCRI_HARDRECONFIG

Configuration will require user to change a jumper

LPCRI_HARDWIRED

Only possible configuration

Figure 3   Creating a Logical Configuration

 CONFIGRET CM_HANDLER OnEnumerate(CONFIGFUNC cf,
    SUBCONFIGFUNC scf, DEVNODE tonode, DEVNODE aboutnode,
    ULONG flags)
    {                           // OnEnumerate
    CONFIGRET code;

    switch (cf)
        {                       // select on function code

    case CONFIG_ENUMERATE:      // cf == 5
        {                       // CONFIG_ENUMERATE
        DEVNODE device;         // DEVNODE for device
        LOG_CONF logconf;       // logical configuration
        RES_DES resource;       // resource descriptor handle
        static IRQ_DES irq = {{0, 0, 0x00FF, 0}};

        code = CM_Create_DevNode(&device, "SCHOOL\\WCO1234\\0000",tonode, 0);
        if (code == CR_ALREADY_SUCH_DEVNODE)
            return CR_SUCCESS;

        CM_Add_Empty_Log_Conf(&logconf, device,
                              LCPRI_NORMAL,
                              BASIC_LOG_CONF | PRIORITY_EQUAL_LAST);
        CM_Add_Res_Des(&resource, logconf, ResType_IRQ, &irq, sizeof(irq), 0);

        return CR_SUCCESS;
        }                       // CONFIG_ENUMERATE

    default:
        return CR_DEFAULT;
        }                       // select on function code
    }                           // OnEnumerate

Figure 4   Possible Alias/Decode Settings for an I/O Resource

Alias

Decode

Meaning

0

0

Card decodes all 16 bits of I/O address

4

3

Card decodes 10 bits of I/O address

16

15

Card decodes 12 bits of I/O address

255

0

Card is a PCI card that can use ports that would otherwise be reserved as 10-bit synonyms for ISA card ports

Figure 5   SCHOOL.VXD

SCHOOL.MAK

 all: school.vxd telepath.vxd

.asm.obj:
ml -coff -DBLD_COFF -DIS_32 -W2 -Zd -c -Cx -DMASM6 -Zi \
-DDEBUG $*.asm

.c.obj:
cl -c -Gs -Zdpl -Od -D_X86_ -YX -W3 -DDEBLEVEL=1 \
-DBLD_COFF -DDEBUG -DIS_32 $*.c

school.vxd : s_ctl.obj $*.obj $*.def $*.res
link @<<
-machine:i386 -def:$*.def -out:$@
-debug -debugtype:map
-map:$*.map -vxd vxdwraps.clb -nodefaultlib
s_ctl.obj $*.obj
<<

telepath.vxd: t_ctl.obj $*.obj $*.def
link @<<
-machine:i386 -def:$*.def -out:$@
-debug -debugtype:map
-map:$*.map -vxd vxdwraps.clb -nodefaultlib
t_ctl.obj $*.obj
<<

SCHOOL.DEF

 VXD SCHOOL DYNAMIC

DESCRIPTION 'Sample Device'

SEGMENTS
_LPTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_LTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_LDATA CLASS 'LCODE' PRELOAD NONDISCARDABLE
_TEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_DATA CLASS 'LCODE' PRELOAD NONDISCARDABLE
CONST CLASS 'LCODE' PRELOAD NONDISCARDABLE
_TLS CLASS 'LCODE' PRELOAD NONDISCARDABLE
_BSS CLASS 'LCODE' PRELOAD NONDISCARDABLE
_MSGTABLE CLASS 'MCODE' PRELOAD NONDISCARDABLE IOPL
_MSGDATA CLASS 'MCODE' PRELOAD NONDISCARDABLE IOPL
_IMSGTABLE CLASS 'MCODE' PRELOAD DISCARDABLE IOPL
_IMSGDATA CLASS 'MCODE' PRELOAD DISCARDABLE IOPL
_ITEXT CLASS 'ICODE' DISCARDABLE
_IDATA LASS 'ICODE' DISCARDABLE
_PTEXT CLASS 'PCODE' NONDISCARDABLE
_PDATA CLASS 'PDATA' NONDISCARDABLE SHARED
_STEXT CLASS 'SCODE' RESIDENT
_SDATA CLASS 'SCODE' RESIDENT
_DBOSTART CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_DBOCODE CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
DBODATA CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_16ICODE CLASS '16ICODE' PRELOAD DISCARDABLE
_RCODE CLASS 'RCODE'
EXPORTS
SCHOOL_DDB @1

S_CTL.ASM

          .386p
include vmm.inc
include debug.inc

Declare_Virtual_Device SCHOOL, 1, 0, SCHOOL_Control, \
Undefined_Device_ID, Undefined_Init_Order

Begin_Control_Dispatch SCHOOL

Control_Dispatch Sys_Dynamic_Device_Init, \
_OnSysDynamicDeviceInit, cCall
Control_Dispatch Sys_Dynamic_Device_Exit, \
_OnSysDynamicDeviceExit, cCall
Control_Dispatch PNP_New_Devnode, _OnPnpNewDevnode, \
cCall, <ebx, edx>

End_Control_Dispatch SCHOOL

end

SCHOOL.C

 #define WANTVXDWRAPS
#include <basedef.h>
#include <vmm.h>
#include <debug.h>
#include <vmmreg.h>
#include <vwin32.h>
#include <vxdwraps.h>
#include <configmg.h>
#include <regstr.h>

#ifdef DEBUG
#define ASSERT(e) if(!(e))\
{Debug_Printf("Assertion failure in " __FILE__ \
", line %d: " #e "\r\n", __LINE__);\
_asm int 1\
}
#else
#define ASSERT(e)
#endif

#define ResType_Telepath ((0x10 << 5) | 5)

typedef struct
{
int allocated; // index of allocated channel (-1 => none)
ULONG requested; // mask for requested channels
} TELEPATH_RESOURCE;

#pragma CM_PAGEABLE_DATA
#pragma CM_PAGEABLE_CODE

CONFIGRET CM_HANDLER OnEnumerate(CONFIGFUNC cf, CONFIGFUNC scf, DEVNODE tonode, DEVNODE aboutnode, ULONG flags);
CONFIGRET CM_HANDLER OnArbitrateTelepath(ARBFUNC af, ULONG refdata, DEVNODE devnode, NODELIST_HEADER h);

REGISTERID arbid;

BOOL OnSysDynamicDeviceInit()
{ // OnSysDynamicDeviceInit
CM_Register_Arbitrator(&arbid, ResType_Telepath, OnArbitrateTelepath, 0, NULL, ARB_GLOBAL);
return TRUE;
} // OnSysDynamicDeviceInit

BOOL OnSysDynamicDeviceExit()
{ // OnSysDynamicDeviceExit
if (arbid)
CM_Deregister_Arbitrator(arbid, 0);
return TRUE;
} // OnSysDynamicDeviceExit

CONFIGRET OnPnpNewDevnode(DEVNODE devnode, DWORD loadtype)
{ // OnPnpNewDevnode
CONFIGRET code;
switch (loadtype)
{ // select function to perform

case DLVXD_LOAD_DEVLOADER: // loadtype == 1
code = CM_Register_Enumerator(devnode, OnEnumerate, CM_REGISTER_ENUMERATOR_HARDWARE);
if (code != CR_SUCCESS)
return code;
return CM_Register_Device_Driver(devnode, NULL, 0,
CM_REGISTER_DEVICE_DRIVER_REMOVABLE);

} // select function to perform

return CR_DEFAULT;
} // OnPnpNewDevnode

CONFIGRET CM_HANDLER OnEnumerate(CONFIGFUNC cf, SUBCONFIGFUNC scf, DEVNODE tonode, DEVNODE aboutnode,
ULONG flags)
{ // OnEnumerate
CONFIGRET code;

#ifdef DEBUG
char toid[MAX_DEVICE_ID_LEN], aboutid[MAX_DEVICE_ID_LEN];
DEBUG_CONFIG_NAMES
char *subfunc = "";
static char *substart[] = {"DYNAMIC_START", "FIRST_START"};
static char *substop[] = {"DYNAMIC_STOP", "HAS_PROBLEM"};
static char *subremove[] = {"DYNAMIC", "SHUTDOWN", "REBOOT"};
static char *subtest[] = {"CAN_STOP", "CAN_REMOVE"};
static char *subapm[] = {"TEST_STANDBY", "TEST_SUSPEND", "TEST_STANDBY_FAILED", "TEST_SUSPEND_FAILED",
"TEST_STANDBY_SUCCEEDED","TEST_SUSPEND_SUCCEEDED",
"RESUME_STANDBY", "RESUME_SUSPEND", "RESUME_CRITICAL", "UI_ALLOWED"};

CM_Get_Device_ID(tonode, toid, sizeof(toid), 0);
CM_Get_Device_ID(aboutnode, aboutid, sizeof(aboutid), 0);
switch (cf)
{ // get subfunction name
case CONFIG_START:
subfunc = substart[scf];
break;
case CONFIG_STOP:
subfunc = substop[scf];
break;
case CONFIG_REMOVE:
subfunc = subremove[scf];
break;
case CONFIG_TEST:
subfunc = subtest[scf];
break;
case CONFIG_APM:
subfunc = subapm[scf];
break;
} // get subfunction name
if (cf < NUM_CONFIG_COMMANDS)
Debug_Printf("SCHOOL ENUMERATOR: %s(%s), to %s, about %s\r\n",
lpszConfigName[cf], subfunc, toid, aboutid);
else
Debug_Printf("SCHOOL ENUMERATOR: %X(%X), to %s, about %s\r\n",
cf, toid, aboutid);
#endif

switch (cf)
{ // select on function code

case CONFIG_SETUP:
{ // CONFIG_SETUP
ULONG length; // length of class name
char class[64]; // device class

length = sizeof(class);
code = CM_Read_Registry_Value(aboutnode, NULL, "Class", REG_SZ, class, &length, CM_REGISTRY_HARDWARE);
if (code == CR_NO_SUCH_VALUE)
{ // new device
CM_Write_Registry_Value(aboutnode, NULL, "Class", REG_SZ, "System", 6, CM_REGISTRY_HARDWARE);
CM_Write_Registry_Value(aboutnode, NULL, "HardwareID", REG_SZ, "WCO1234", 6, CM_REGISTRY_HARDWARE);
} // new device
return CR_SUCCESS;
} // CONFIG_SETUP

case CONFIG_ENUMERATE: // cf == 5
{ // CONFIG_ENUMERATE
DEVNODE device; // DEVNODE for device
ULONG length; // length of class name
char class[64]; // device class
LOG_CONF logconf; // logical configuration
RES_DES resource; // resource descriptor handle

static IRQ_RESOURCE irq = {{0, 0, 0xFFFF, 0}};
static TELEPATH_RESOURCE telepath = {-1, 0xFF};

code = CM_Create_DevNode(&device, "SCHOOL\\WCO1234\\0000", tonode, 0);
if (code == CR_ALREADY_SUCH_DEVNODE)
return CR_SUCCESS;

CM_Add_Empty_Log_Conf(&logconf, device, LCPRI_NORMAL,
BASIC_LOG_CONF | PRIORITY_EQUAL_LAST);
CM_Add_Res_Des(&resource, logconf, ResType_IRQ, &irq, sizeof(irq), 0);
CM_Add_Res_Des(&resource, logconf, ResType_Telepath, &telepath, sizeof(telepath), 0);

return CR_SUCCESS;
} // CONFIG_ENUMERATE

default:
return CR_DEFAULT;
} // select on function code
} // OnEnumerate

/////////////////////////////////////////////////////////////// // Telepathic I/O channel resource arbitrator
typedef struct nodelistheader_s *NODELISTHEADER;
typedef struct nodelist_s *PNODE;

typedef struct
{ // allocation placeholder
PNODE node; // current node
TELEPATH_RESOURCE* pres; // current resource descriptor
} ALLOCPLACE, *PALLOCPLACE; // allocation placeholder

int bitcount(ULONG mask)
{ // bitcount
int nbits = 0;
while (mask)
{ // count bits
if (mask & 1)
++nbits;
mask >>= 1;
} // count bits
return nbits;
} // bitcount

void sortnodes(NODELISTHEADER h)
{ // sortnodes
PNODE node = h->nlh_Head;
while (node)
{ // for each node
RES_DES hres = (RES_DES) node->nl_Test_Req;
#define pres ((TELEPATH_RESOURCE *) hres)

node->nl_ulSortDWord = 0;
while (CM_Get_Next_Res_Des(&hres, hres, ResType_Telepath, NULL, 0) == CR_SUCCESS)
node->nl_ulSortDWord += bitcount(pres->requested);
node = node->nl_Next;

#undef pres
} // for each node
CM_Sort_NodeList((NODELIST_HEADER) h, 0);
} // sortnodes

void release(NODELISTHEADER h, PULONG pmap)
{ // release
PNODE node = h->nlh_Head;
while (node)
{ // for each node
LOG_CONF logconf;

if (CM_Get_First_Log_Conf(&logconf,(DEVNODE) node->nl_ItsDevNode, ALLOC_LOG_CONF)== CR_SUCCESS)
{ // release channel(s)
RES_DES hres = (RES_DES) logconf;
#define pres ((TELEPATH_RESOURCE *) hres)
while (CM_Get_Next_Res_Des(&hres, hres, ResType_Telepath, NULL, 0) == CR_SUCCESS)
if (pres->allocated >= 0)
*pmap |= 1 << pres->allocated;
#undef pres
} // release channel(s)
node = node->nl_Next;
} // for each node
} // release

BOOL nextres(PALLOCPLACE p)
{ // nextres
ASSERT(p->node && p->node->nl_Test_Req);
if (!p->pres)
p->pres = (TELEPATH_RESOURCE *) p->node->nl_Test_Req;
while (CM_Get_Next_Res_Des((RES_DES*) &p->pres, (RES_DES) p->pres, ResType_Telepath, NULL, 0) != CR_SUCCESS)
{ // no more of our resource
if (!(p->node = p->node->nl_Next))
return FALSE; // no more nodes in the list
p->pres = (TELEPATH_RESOURCE *) p->node->nl_Test_Req;
} // no more of our resource
return TRUE;
} // nextres

BOOL allocate(PALLOCPLACE p, PULONG pmap)
{ // allocate
int channel;

if (!nextres(p))
return TRUE;

for (p->pres->allocated = -1, channel = 0; channel < 32; ++channel)
{ // try to allocate a channel
ALLOCPLACE place = *p;
ULONG mask = 1 << channel;
ULONG tempmap = *pmap;
if ( !(tempmap & mask) || !(place.pres->requested & mask) )
continue;
tempmap &= ~mask; // do trial allocation
if (allocate(&place, &tempmap))
{ // successful allocation
*pmap = tempmap;
p->pres->allocated = channel;
return TRUE;
} // successful allocation
} // try to allocate a channel
} // allocate

BOOL reallocate(NODELISTHEADER h, PULONG pmap, BOOL forced)
{ // reallocate
PNODE node = h->nlh_Head;

while (node)
{ // for each node
RES_DES hres = (RES_DES) node->nl_Test_Req;
#define pres ((TELEPATH_RESOURCE *) hres)

while (CM_Get_Next_Res_Des(&hres, hres,ResType_Telepath, NULL, 0) == CR_SUCCESS)
{ // requires our resource
ULONG mask;

ASSERT(pres->allocated >= 0);
mask = 1 << pres->allocated;
if ((*pmap & mask) && !forced)
return FALSE; // one or more still in use
*pmap &= ~mask;
} // requires our resource
node = node->nl_Next;

#undef pres
} // for each node
return TRUE;
} // reallocate

CONFIGRET CM_HANDLER OnArbitrateTelepath(ARBFUNC af, ULONG refdata, DEVNODE devnode, NODELIST_HEADER h)
{ // OnArbitrateTelepath
static ULONG free_map = 0xFF; // real allocation map
static ULONG free_copy = 0xDEADBEEF; // copy of map

switch (af)
{ // select on function

case ARB_TEST_ALLOC: // af == 0
{ // ARB_TEST_ALLOC
ALLOCPLACE place = {((NODELISTHEADER)h)->nlh_Head, NULL};
sortnodes((NODELISTHEADER) h);
free_copy = free_map;
release((NODELISTHEADER) h, &free_copy);

if (allocate(&place, &free_copy))
return CR_SUCCESS;
else
return CR_FAILURE;
} // ARB_TEST_ALLOC

case ARB_RETEST_ALLOC: // af == 1
case ARB_FORCE_ALLOC: // af == 6
free_copy = free_map;
release((NODELISTHEADER) h, &free_copy);

if (reallocate((NODELISTHEADER) h, &free_copy, af == ARB_FORCE_ALLOC))
return CR_SUCCESS;
else
return CR_FAILURE;

case ARB_SET_ALLOC: // af == 2
ASSERT(free_copy != 0xDEADBEEF);
free_map = free_copy;
free_copy = 0xDEADBEEF;
return CR_SUCCESS;

case ARB_RELEASE_ALLOC: // af == 3
free_copy = 0xDEADBEEF;
return CR_SUCCESS;

case ARB_QUERY_FREE: // af == 4
{ // ARB_QUERY_FREE
struct arbitfree_s *p = (struct arbitfree_s *) h;
p->af_SizeOfInfo = sizeof(ULONG);
p->af_PointerToInfo = (PVOID*) &free_map;
return CR_SUCCESS;
} // ARB_QUERY_FREE

case ARB_REMOVE: // af == 5
return CR_SUCCESS;

default:
return CR_DEFAULT;
} // select on function
} // OnArbitrateTelepath

TELEPATH.DEF

T_CTL.ASM

These two files are essentially the same as SCHOOL.DEF and S_CTL.ASM, respectively. SCHOOL.MAK also builds TELEPATH.VXD.

TELEPATH.C

 #define WANTVXDWRAPS
#include <basedef.h>
#include <vmm.h>
#include <debug.h>
#include <vmmreg.h>
#include <vxdwraps.h>
#include <configmg.h>
#include <regstr.h>

static BYTE irq; // allocated IRQ

CONFIGRET OnConfigure(CONFIGFUNC cf, SUBCONFIGFUNC scf, DEVNODE devnode, DWORD refdata, ULONG flags);

BOOL OnSysDynamicDeviceInit()
{ // OnSysDynamicDeviceInit
return TRUE;
} // OnSysDynamicDeviceInit

BOOL OnSysDynamicDeviceExit()
{ // OnSysDynamicDeviceExit
return TRUE;
} // OnSysDynamicDeviceExit

CONFIGRET OnPnpNewDevnode(DEVNODE devnode, DWORD loadtype)
{ // OnPnpNewDevnode
switch (loadtype)
{ // select function to perform

case DLVXD_LOAD_DEVLOADER: // loadtype == 2
return CM_Register_Device_Driver(devnode, OnConfigure, 0,
(CM_REGISTER_DEVICE_DRIVER_REMOVABLE | CM_REGISTER_DEVICE_DRIVER_DISABLEABLE));
} // select function to perform

return CR_DEFAULT;
} // OnPnpNewDevnode

CONFIGRET OnConfigure(CONFIGFUNC cf, SUBCONFIGFUNC scf, DEVNODE devnode, DWORD refdata, ULONG flags)
{ // OnConfigure

#ifdef DEBUG
char id[MAX_DEVICE_ID_LEN];
DEBUG_CONFIG_NAMES
char *subfunc = "";
static char *substart[] = {"DYNAMIC_START", "FIRST_START"};
static char *substop[] = {"DYNAMIC_STOP", "HAS_PROBLEM"};
static char *subremove[] = {"DYNAMIC", "SHUTDOWN",
"REBOOT"};
static char *subtest[] = {"CAN_STOP", "CAN_REMOVE"};
static char *subapm[] ={"TEST_STANDBY", "TEST_SUSPEND", "TEST_STANDBY_FAILED", "TEST_SUSPEND_FAILED",
"TEST_STANDBY_SUCCEEDED", "TEST_SUSPEND_SUCCEEDED",
"RESUME_STANDBY", "RESUME_SUSPEND", "RESUME_CRITICAL", "UI_ALLOWED"};

switch (cf)
{ // get subfunction name
case CONFIG_START:
subfunc = substart[scf];
break;
case CONFIG_STOP:
subfunc = substop[scf];
break;
case CONFIG_REMOVE:
subfunc = subremove[scf];
break;
case CONFIG_TEST:
subfunc = subtest[scf];
break;
case CONFIG_APM:
subfunc = subapm[scf];
break;
} // get subfunction name

CM_Get_Device_ID(devnode, id, sizeof(id), 0);
if (cf < NUM_CONFIG_COMMANDS)
Debug_Printf("TELEPATH DRIVER: %s(%s), %s\r\n", lpszConfigName[cf], subfunc, id);
else
Debug_Printf("TELEPATH DRIVER: %X(%X), %s\r\n", cf, scf, id);
#endif

switch (cf)
{ // select on function code

case CONFIG_START: // cf == 1
{ // CONFIG_START

CMCONFIG config;
CM_Get_Alloc_Log_Conf(&config, devnode, CM_GET_ALLOC_LOG_CONF_ALLOC);
irq = config.bIRQRegisters[0];

{
#define ResType_Telepath ((0x10 << 5) | 5)

typedef struct
{ // telepath resource
ULONG allocated; // mask for allocated channel
ULONG requested; // mask for requested channels
} TELEPATH_RESOURCE; // telepath resource

LOG_CONF logconf;
RES_DES hres;
#define pres ((TELEPATH_RESOURCE*) hres)

CM_Get_First_Log_Conf(&logconf, devnode, ALLOC_LOG_CONF);
if (CM_Get_Next_Res_Des(&hres, (RES_DES) logconf, ResType_Telepath, NULL, 0) == CR_SUCCESS)
{ // has telepath channel
// do something with pres->allocated
} // has telepath channel

#undef pres
}

// Here we would initialize the device, doing things
// like VPICD_Virtualize_IRQ, etc.

return CR_SUCCESS;
} // CONFIG_START

case CONFIG_REMOVE: // cf == 4
case CONFIG_STOP: // cf == 2

irq = 255;
return CR_SUCCESS;

default:
return CR_DEFAULT;
} // select on function code
} // OnConfigure

Figure 6   Arbitrator Function Codes

Function Code

Description

ARB_TEST_ALLOC

Perform trial allocation of resource

ARB_RETEST_ALLOC

Verify previous trial allocation

ARB_FORCE_ALLOC

Retest but don't fail previous trial allocation

ARG_SET_ALLOC

Commit previous trial allocation

ARB_RELEASE_ALLOC

Discard previous trial allocation

ARB_QUERY_FREE

Return information about free resources

ARB_REMOVE

Prepare for removal of arbitrator

Figure 7   nodelistheader_s and nodelist_s Structures

 struct nodelistheader_s
    {                               // node list header
    struct nodelist_s* nlh_Head;    // 00 head of list
    struct nodelist_s* nlh_Tail;    // 04 tail of list
    };                              // 08 node list header

struct nodelist_s
    {                                // node list element
    struct nodelist_s* nl_Next;      // 00 chain to next element
    struct nodelist_s* nl_Prev;      // 04 chain to previous element
    struct devnode_s* nl_ItsDevNode; // 08 a device node that needs resources
    struct Log_Conf* nl_Test_Req;    // 0C a logical configuration for that 
                                     // DEVNODE
    ULONG nl_ulSortDWord;            // 10 sort order for CM_Sort_NodeList
    };                               // 14 node list element

Figure 8   Configuration Functions for Device Drivers

Function

Description

CONFIG_APM

Power management event is occurring

CONFIG_CALLBACK

Someone did a CM_CallBack_-Device_Driver

CONFIG_FILTER

Filter (restrict) logical configurations

CONFIG_PREREMOVE

Prepare for removal of device

CONFIG_PREREMOVE2

Prepare for removal of device

CONFIG_PRESHUTDOWN

System is about to shut down

CONFIG_REMOVE

Device is being removed from the system

CONFIG_SHUTDOWN

System is shutting down

CONFIG_START

Start using the allocated configuration

CONFIG_STOP

Stop using the current configuration

CONFIG_TEST

Can you stop using configuration, or can you be removed?

CONFIG_TEST_FAILED

Previous CONFIG_TEST failed

CONFIG_TEST_SUCCEEDED

Previous CONFIG_TEST succeeded

CONFIG_VERIFY_DEVICE

Is your legacy card present?

Figure 9   Getting a Device's Configuration

 CONFIGRET OnConfigure(CONFIGFUNC cf, SUBCONFIGFUNC scf,
    DEVNODE devnode, DWORD refdata, ULONG flags)
    {                           // OnConfigure
    switch (cf)
        {                       // select on function code

    case CONFIG_START:          // cf == 1
        {                       // CONFIG_START
        CMCONFIG config;
        CM_Get_Alloc_Log_Conf(&config, devnode,
                              CM_GET_ALLOC_LOG_CONF_ALLOC);
        irq = config.bIRQRegisters[0]; // for example
        // [virtualize IRQ and do other initialization]
        return CR_SUCCESS;
        }                       // CONFIG_START
    
    default:
        return CR_DEFAULT;
        }                       // select on function code
    }                           // OnConfigure

Figure 10   CMCONFIG Structure

 typedef struct Config_Buff_s {
WORD    wNumMemWindows;                 // 00 Num memory windows
DWORD   dMemBase[MAX_MEM_REGISTERS];    // 02 Memory window base [9]
DWORD   dMemLength[MAX_MEM_REGISTERS];  // 26 Memory window length [9]
WORD    wMemAttrib[MAX_MEM_REGISTERS];  // 4A Memory window Attrib [9]
WORD    wNumIOPorts;                    // 5C Num IO ports
WORD    wIOPortBase[MAX_IO_PORTS];      // 5E I/O port base [20]
WORD    wIOPortLength[MAX_IO_PORTS];    // 86 I/O port length [20]
WORD    wNumIRQs;                       // AE Num IRQ info
BYTE    bIRQRegisters[MAX_IRQS];        // B0 IRQ list [7]
BYTE    bIRQAttrib[MAX_IRQS];           // B7 IRQ Attrib list [7]
WORD    wNumDMAs;                       // BE Num DMA channels
BYTE    bDMALst[MAX_DMA_CHANNELS];      // C0 DMA list [7]
WORD    wDMAAttrib[MAX_DMA_CHANNELS];   // C7 DMA Attrib list [7]
BYTE    bReserved1[3];                  // D5 Reserved
} CMCONFIG;                             // D8

Figure 14   SCHOOLUI.DLL

SCHOOLUI.DEF

 LIBRARY   SCHOOLUI
EXETYPE WINDOWS
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE SINGLE
HEAPSIZE 1024
EXPORTS
WEP PRIVATE
EnumPropPages @1

SCHOOLUI.RC

 //Microsoft App Studio generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
///////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

///////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS


///////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_STATUS DIALOG DISCARDABLE 0, 0, 142, 106
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION |
WS_SYSMENU
CAPTION "Status"
FONT 8, "MS Sans Serif"
BEGIN
LTEXT "Telepathic Channel:",IDC_STATIC,3,9,74,8
LTEXT "(None)",IDC_CHANNEL,93,9,35,7
END

#ifdef APSTUDIO_INVOKED
///////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END

2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END

3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END

///////////////////////////////////////////////////////////////
#endif // APSTUDIO_INVOKED


#ifndef APSTUDIO_INVOKED
///////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


///////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

RESOURCE.H

 //{{NO_DEPENDENCIES}}
// App Studio generated include file.
// Used by SCHOOLUI.RC
//
#define SCHOOL 101
#define IDD_STATUS 103
#define IDC_CHANNEL 1000
#define IDC_STATIC -1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NEXT_RESOURCE_VALUE 105
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

SCHOOLUI.C

 #include <windows.h>
#include <commctrl.h>
#include <setupx.h>

#define EXPORT __export

#define Not_VxD // to get ring-3 dcls
#include <vmm.h>
#define MIDL_PASS // suppress 32-bit only #pragma pack(push)
#include <configmg.h>

#include "resource.h"

BOOL WINAPI EXPORT StatusDlgProc(HWND, UINT, WPARAM, LPARAM);

///////////////////////////////////////////////////////////////

BOOL WINAPI EXPORT EnumPropPages(LPDEVICE_INFO pdi,
LPFNADDPROPSHEETPAGE AddPage, LPARAM lParam)
{ // EnumPropPages
PROPSHEETPAGE status; // status property page
HPROPSHEETPAGE hstatus;

status.dwSize = sizeof(PROPSHEETPAGE);
status.dwFlags = PSP_USETITLE;
_asm mov status.hInstance, ds
status.pszTemplate = MAKEINTRESOURCE(IDD_STATUS);
status.hIcon = NULL;
status.pszTitle = "Status";
status.pfnDlgProc = StatusDlgProc;
status.lParam = (LPARAM) pdi->dnDevnode;
status.pfnCallback = NULL;

hstatus = CreatePropertySheetPage(&status);
if (!hstatus)
return TRUE;


if (!AddPage(hstatus, lParam))
DestroyPropertySheetPage(hstatus);
return TRUE;
} // EnumPropPages

///////////////////////////////////////////////////////////////

BOOL WINAPI EXPORT StatusDlgProc(HWND hdlg, UINT msg,
WPARAM wParam, LPARAM lParam)
{ // StatusDlgProc
switch (msg)
{ // process message

case WM_INITDIALOG:
{ // WM_INITDIALOG

#define ResType_Telepath ((0x10 << 5) | 5)

typedef struct
{ // telepath resource
ULONG allocated; // mask for allocated channel
ULONG requested; // mask for requested channels
} TELEPATH_RESOURCE;// telepath resource

LOG_CONF logconf;
RES_DES hres;
DEVNODE devnode = (DEVNODE) ((LPPROPSHEETPAGE) lParam)->lParam;

if (CM_Get_First_Log_Conf(&logconf, devnode, ALLOC_LOG_CONF)== CR_SUCCESS
&& CM_Get_Next_Res_Des(&hres, (RES_DES) logconf,
ResType_Telepath, NULL, 0) == CR_SUCCESS)
{ // has telepath channel
TELEPATH_RESOURCE res;

CM_Get_Res_Des_Data(hres, &res, sizeof(res), 0);
if (res.allocated >= 0)
SetDlgItemInt(hdlg, IDC_CHANNEL, res.allocated, FALSE);
} // has telepath channel
} // WM_INITDIALOG

} // process message
return FALSE;
} // StatusDlgProc