mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1578 lines
32 KiB
1578 lines
32 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
detibm.c
|
|
|
|
Abstract:
|
|
|
|
This is the main file for the autodetection DLL for all the ibmtok.sys
|
|
which MS is shipping with Windows NT.
|
|
|
|
Author:
|
|
|
|
Sean Selitrennikoff (SeanSe) October 1992.
|
|
|
|
Environment:
|
|
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
|
|
#include <windows.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "ntddnetd.h"
|
|
#include "detect.h"
|
|
|
|
|
|
#if DBG
|
|
|
|
#define DBGPRINT(x) DbgPrint x
|
|
|
|
#else
|
|
|
|
#define DBGPRINT(x)
|
|
|
|
#endif
|
|
|
|
//
|
|
// Individual card detection routines
|
|
//
|
|
|
|
BOOLEAN
|
|
IbmtokCardAt(
|
|
IN INTERFACE_TYPE InterfaceType,
|
|
IN ULONG BusNumber,
|
|
IN ULONG IoBaseAddress,
|
|
OUT PUCHAR Interrupt,
|
|
OUT PULONG MemoryAddress,
|
|
OUT PULONG MemoryLength
|
|
);
|
|
|
|
|
|
#ifdef WORKAROUND
|
|
|
|
UCHAR IbmtokFirstTime = 1;
|
|
|
|
//
|
|
// List of all the adapters supported in this file, this cannot be > 256
|
|
// because of the way tokens are generated.
|
|
//
|
|
//
|
|
// NOTE : If you change the index of an adapter, be sure the change it in
|
|
// IbmtokQueryCfgHandler() and IbmtokVerifyCfgHandler() as well!
|
|
//
|
|
|
|
static ADAPTER_INFO Adapters[] = {
|
|
|
|
{
|
|
1000,
|
|
L"IBMTOK",
|
|
L"IRQ 2 100 IRQTYPE 2 100 IOADDR 1 100 IOADDRLENGTH 2 100 MEMADDR 2 100 MEMADDRLENGTH 2 100 ",
|
|
NULL,
|
|
800
|
|
|
|
}
|
|
|
|
};
|
|
|
|
#else
|
|
|
|
//
|
|
// List of all the adapters supported in this file, this cannot be > 256
|
|
// because of the way tokens are generated.
|
|
//
|
|
//
|
|
// NOTE : If you change the index of an adapter, be sure the change it in
|
|
// IbmtokQueryCfgHandler() and IbmtokVerifyCfgHandler() as well!
|
|
//
|
|
|
|
static ADAPTER_INFO Adapters[] = {
|
|
|
|
{
|
|
1000,
|
|
L"IBMTOK",
|
|
L"IRQ\0"
|
|
L"2\0"
|
|
L"100\0"
|
|
L"IRQTYPE\0"
|
|
L"2\0"
|
|
L"100\0"
|
|
L"IOADDR\0"
|
|
L"1\0"
|
|
L"100\0"
|
|
L"IOADDRLENGTH\0"
|
|
L"2\0"
|
|
L"100\0"
|
|
L"MEMADDR\0"
|
|
L"2\0"
|
|
L"100\0"
|
|
L"MEMADDRLENGTH\0"
|
|
L"2\0"
|
|
L"100\0",
|
|
NULL,
|
|
800
|
|
|
|
}
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
//
|
|
// Structure for holding state of a search
|
|
//
|
|
|
|
typedef struct _SEARCH_STATE
|
|
{
|
|
ULONG IoBaseAddress;
|
|
UCHAR Interrupt;
|
|
ULONG MemoryAddress;
|
|
ULONG MemoryLength;
|
|
}
|
|
SEARCH_STATE,
|
|
*PSEARCH_STATE;
|
|
|
|
#define PRIMARY 0xA20
|
|
#define SECONDARY 0xA24
|
|
|
|
|
|
//
|
|
// This is an array of search states. We need one state for each type
|
|
// of adapter supported.
|
|
//
|
|
|
|
static SEARCH_STATE SearchStates[sizeof(Adapters) / sizeof(ADAPTER_INFO)] = {0};
|
|
|
|
|
|
//
|
|
// Structure for holding a particular adapter's complete information
|
|
//
|
|
typedef struct _IBMTOK_ADAPTER
|
|
{
|
|
LONG CardType;
|
|
INTERFACE_TYPE InterfaceType;
|
|
ULONG BusNumber;
|
|
ULONG IoBaseAddress;
|
|
UCHAR Interrupt;
|
|
ULONG MemoryAddress;
|
|
ULONG MemoryLength;
|
|
}
|
|
IBMTOK_ADAPTER,
|
|
*PIBMTOK_ADAPTER;
|
|
|
|
extern
|
|
LONG
|
|
IbmtokIdentifyHandler(
|
|
IN LONG lIndex,
|
|
IN WCHAR * pwchBuffer,
|
|
IN LONG cwchBuffSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns information about the netcards supported by
|
|
this file.
|
|
|
|
Arguments:
|
|
|
|
lIndex - The index of the netcard being address. The first
|
|
cards information is at index 1000, the second at 1100, etc.
|
|
|
|
pwchBuffer - Buffer to store the result into.
|
|
|
|
cwchBuffSize - Number of bytes in pwchBuffer
|
|
|
|
Return Value:
|
|
|
|
0 if nothing went wrong, else the appropriate WINERROR.H value.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
LONG NumberOfAdapters;
|
|
LONG Code = lIndex % 100;
|
|
LONG Length;
|
|
LONG i;
|
|
|
|
NumberOfAdapters = sizeof(Adapters) / sizeof(ADAPTER_INFO);
|
|
|
|
#ifdef WORKAROUND
|
|
|
|
if (IbmtokFirstTime) {
|
|
|
|
IbmtokFirstTime = 0;
|
|
|
|
for (i = 0; i < NumberOfAdapters; i++) {
|
|
|
|
Length = UnicodeStrLen(Adapters[i].Parameters);
|
|
|
|
for (; Length > 0; Length--) {
|
|
|
|
if (Adapters[i].Parameters[Length] == L' ') {
|
|
|
|
Adapters[i].Parameters[Length] = UNICODE_NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
lIndex = lIndex - Code;
|
|
|
|
if (((lIndex / 100) - 10) < NumberOfAdapters) {
|
|
|
|
for (i=0; i < NumberOfAdapters; i++) {
|
|
|
|
if (Adapters[i].Index == lIndex) {
|
|
|
|
switch (Code) {
|
|
|
|
case 0:
|
|
|
|
//
|
|
// Find the string length
|
|
//
|
|
|
|
Length = UnicodeStrLen(Adapters[i].InfId);
|
|
|
|
Length ++;
|
|
|
|
if (cwchBuffSize < Length) {
|
|
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
|
|
}
|
|
|
|
memcpy((PVOID)pwchBuffer, Adapters[i].InfId, Length * sizeof(WCHAR));
|
|
break;
|
|
|
|
case 3:
|
|
|
|
//
|
|
// Maximum value is 1000
|
|
//
|
|
|
|
if (cwchBuffSize < 5) {
|
|
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
|
|
}
|
|
|
|
wsprintf((PVOID)pwchBuffer, L"%d", Adapters[i].SearchOrder);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return(ERROR_INVALID_PARAMETER);
|
|
|
|
}
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(ERROR_INVALID_PARAMETER);
|
|
|
|
}
|
|
|
|
return(ERROR_NO_MORE_ITEMS);
|
|
|
|
}
|
|
|
|
|
|
extern
|
|
LONG IbmtokFirstNextHandler(
|
|
IN LONG lNetcardId,
|
|
IN INTERFACE_TYPE InterfaceType,
|
|
IN ULONG BusNumber,
|
|
IN BOOL fFirst,
|
|
OUT PVOID *ppvToken,
|
|
OUT LONG *lConfidence
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds the instances of a physical adapter identified
|
|
by the NetcardId.
|
|
|
|
NOTE: That for now there is only one card supported by this file,
|
|
so we ignore the specific FirstNext handler in Adapters to save
|
|
some processing time.
|
|
|
|
Arguments:
|
|
|
|
lNetcardId - The index of the netcard being address. The first
|
|
cards information is id 1000, the second id 1100, etc.
|
|
|
|
InterfaceType - Either Isa, or Eisa.
|
|
|
|
BusNumber - The bus number of the bus to search.
|
|
|
|
fFirst - TRUE is we are to search for the first instance of an
|
|
adapter, FALSE if we are to continue search from a previous stopping
|
|
point.
|
|
|
|
ppvToken - A pointer to a handle to return to identify the found
|
|
instance
|
|
|
|
lConfidence - A pointer to a long for storing the confidence factor
|
|
that the card exists.
|
|
|
|
Return Value:
|
|
|
|
0 if nothing went wrong, else the appropriate WINERROR.H value.
|
|
|
|
--*/
|
|
|
|
{
|
|
NETDTECT_RESOURCE Resource;
|
|
|
|
if ((InterfaceType != Isa) && (InterfaceType != Eisa))
|
|
{
|
|
*lConfidence = 0;
|
|
return(0);
|
|
}
|
|
|
|
if (lNetcardId != 1000)
|
|
{
|
|
*lConfidence = 0;
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// If fFirst, reset search state
|
|
//
|
|
if (fFirst)
|
|
{
|
|
SearchStates[0].IoBaseAddress = PRIMARY;
|
|
|
|
//
|
|
// We are the first isa nic so we acquire all
|
|
// the isa ranges.
|
|
//
|
|
AcquireAllPcmciaResources();
|
|
}
|
|
else if (SearchStates[0].IoBaseAddress == SECONDARY)
|
|
{
|
|
//
|
|
// We've exhausted the possibilities
|
|
//
|
|
*lConfidence = 0;
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
SearchStates[0].IoBaseAddress = SECONDARY;
|
|
}
|
|
|
|
|
|
//
|
|
// Find an adapter
|
|
//
|
|
if (!IbmtokCardAt(
|
|
InterfaceType,
|
|
BusNumber,
|
|
SearchStates[0].IoBaseAddress,
|
|
&SearchStates[0].Interrupt,
|
|
&SearchStates[0].MemoryAddress,
|
|
&SearchStates[0].MemoryLength))
|
|
{
|
|
//
|
|
// Try again.
|
|
//
|
|
if (SearchStates[0].IoBaseAddress == SECONDARY)
|
|
{
|
|
*lConfidence = 0;
|
|
return(0);
|
|
}
|
|
|
|
if (!IbmtokCardAt(
|
|
InterfaceType,
|
|
BusNumber,
|
|
SECONDARY,
|
|
&SearchStates[0].Interrupt,
|
|
&SearchStates[0].MemoryAddress,
|
|
&SearchStates[0].MemoryLength))
|
|
{
|
|
*lConfidence = 0;
|
|
return(0);
|
|
}
|
|
|
|
SearchStates[0].IoBaseAddress = SECONDARY;
|
|
}
|
|
|
|
//
|
|
// In this module I use the token as follows: Remember that
|
|
// the token can only be 2 bytes long (the low 2) because of
|
|
// the interface to the upper part of this DLL.
|
|
//
|
|
// The high bit of the short is boolean for ISA (else, EISA).
|
|
// The rest of the high byte is the the bus number.
|
|
// The low byte is the driver index number into Adapters.
|
|
//
|
|
// NOTE: This presumes that there are < 129 buses in the
|
|
// system. Is this reasonable?
|
|
//
|
|
if (InterfaceType == Isa)
|
|
{
|
|
*ppvToken = (PVOID)0x8000;
|
|
}
|
|
else
|
|
{
|
|
*ppvToken = (PVOID)0x0;
|
|
}
|
|
|
|
*ppvToken = (PVOID)(((ULONG)*ppvToken) | ((BusNumber & 0x7F) << 8));
|
|
|
|
*ppvToken = (PVOID)(((ULONG)*ppvToken) | 0); // index
|
|
|
|
*lConfidence = 100;
|
|
return(0);
|
|
}
|
|
|
|
extern
|
|
LONG
|
|
IbmtokOpenHandleHandler(
|
|
IN PVOID pvToken,
|
|
OUT PVOID *ppvHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes a token returned by FirstNext and converts it
|
|
into a permanent handle.
|
|
|
|
Arguments:
|
|
|
|
Token - The token.
|
|
|
|
ppvHandle - A pointer to the handle, so we can store the resulting
|
|
handle.
|
|
|
|
Return Value:
|
|
|
|
0 if nothing went wrong, else the appropriate WINERROR.H value.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIBMTOK_ADAPTER Handle;
|
|
LONG AdapterNumber;
|
|
ULONG BusNumber;
|
|
INTERFACE_TYPE InterfaceType;
|
|
|
|
//
|
|
// Get info from the token
|
|
//
|
|
if (((ULONG)pvToken) & 0x8000)
|
|
{
|
|
InterfaceType = Isa;
|
|
}
|
|
else
|
|
{
|
|
InterfaceType = Eisa;
|
|
}
|
|
|
|
|
|
BusNumber = (ULONG)(((ULONG)pvToken >> 8) & 0x7F);
|
|
|
|
AdapterNumber = ((ULONG)pvToken) & 0xFF;
|
|
|
|
//
|
|
// Store information
|
|
//
|
|
Handle = (PIBMTOK_ADAPTER)DetectAllocateHeap(sizeof(IBMTOK_ADAPTER));
|
|
if (Handle == NULL)
|
|
{
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
//
|
|
// Copy across address
|
|
//
|
|
Handle->IoBaseAddress = SearchStates[(ULONG)AdapterNumber].IoBaseAddress;
|
|
Handle->Interrupt = SearchStates[(ULONG)AdapterNumber].Interrupt;
|
|
Handle->MemoryAddress = SearchStates[(ULONG)AdapterNumber].MemoryAddress;
|
|
Handle->MemoryLength = SearchStates[(ULONG)AdapterNumber].MemoryLength;
|
|
Handle->CardType = Adapters[AdapterNumber].Index;
|
|
Handle->InterfaceType = InterfaceType;
|
|
Handle->BusNumber = BusNumber;
|
|
|
|
*ppvHandle = (PVOID)Handle;
|
|
|
|
return(0);
|
|
}
|
|
|
|
extern
|
|
LONG IbmtokCreateHandleHandler(
|
|
IN LONG lNetcardId,
|
|
IN INTERFACE_TYPE InterfaceType,
|
|
IN ULONG BusNumber,
|
|
OUT PVOID *ppvHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to force the creation of a handle for cases
|
|
where a card is not found via FirstNext, but the user says it does
|
|
exist.
|
|
|
|
Arguments:
|
|
|
|
lNetcardId - The id of the card to create the handle for.
|
|
|
|
InterfaceType - Isa or Eisa.
|
|
|
|
BusNumber - The bus number of the bus in the system.
|
|
|
|
ppvHandle - A pointer to the handle, for storing the resulting handle.
|
|
|
|
Return Value:
|
|
|
|
0 if nothing went wrong, else the appropriate WINERROR.H value.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIBMTOK_ADAPTER Handle;
|
|
LONG NumberOfAdapters;
|
|
LONG i;
|
|
NETDTECT_RESOURCE Resource;
|
|
|
|
if ((InterfaceType != Isa) && (InterfaceType != Eisa))
|
|
{
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
NumberOfAdapters = sizeof(Adapters) / sizeof(ADAPTER_INFO);
|
|
|
|
for (i = 0; i < NumberOfAdapters; i++)
|
|
{
|
|
if (Adapters[i].Index == lNetcardId)
|
|
{
|
|
//
|
|
// Store information
|
|
//
|
|
Handle = (PIBMTOK_ADAPTER)DetectAllocateHeap(sizeof(IBMTOK_ADAPTER));
|
|
if (Handle == NULL)
|
|
{
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
//
|
|
// Copy across memory address
|
|
//
|
|
Handle->IoBaseAddress = PRIMARY;
|
|
Handle->Interrupt = 7;
|
|
|
|
Handle->CardType = lNetcardId;
|
|
Handle->InterfaceType = InterfaceType;
|
|
Handle->BusNumber = BusNumber;
|
|
|
|
//
|
|
// Acquire the port.
|
|
//
|
|
Resource.InterfaceType = InterfaceType;
|
|
Resource.BusNumber = BusNumber;
|
|
Resource.Type = NETDTECT_PORT_RESOURCE;
|
|
Resource.Value = PRIMARY;
|
|
Resource.Length = 0x4;
|
|
Resource.Flags = 0;
|
|
|
|
DetectTemporaryClaimResource(&Resource);
|
|
|
|
*ppvHandle = (PVOID)Handle;
|
|
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
extern
|
|
LONG
|
|
IbmtokCloseHandleHandler(
|
|
IN PVOID pvHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This frees any resources associated with a handle.
|
|
|
|
Arguments:
|
|
|
|
pvHandle - The handle.
|
|
|
|
Return Value:
|
|
|
|
0 if nothing went wrong, else the appropriate WINERROR.H value.
|
|
|
|
--*/
|
|
|
|
{
|
|
DetectFreeHeap(pvHandle);
|
|
|
|
return(0);
|
|
}
|
|
|
|
LONG
|
|
IbmtokQueryCfgHandler(
|
|
IN PVOID pvHandle,
|
|
OUT WCHAR *pwchBuffer,
|
|
IN LONG cwchBuffSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calls the appropriate driver's query config handler to
|
|
get the parameters for the adapter associated with the handle.
|
|
|
|
Arguments:
|
|
|
|
pvHandle - The handle.
|
|
|
|
pwchBuffer - The resulting parameter list.
|
|
|
|
cwchBuffSize - Length of the given buffer in WCHARs.
|
|
|
|
Return Value:
|
|
|
|
0 if nothing went wrong, else the appropriate WINERROR.H value.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIBMTOK_ADAPTER Adapter = (PIBMTOK_ADAPTER)(pvHandle);
|
|
NTSTATUS NtStatus;
|
|
ULONG IoBaseAddress = 0;
|
|
LONG OutputLengthLeft = cwchBuffSize;
|
|
LONG CopyLength;
|
|
ULONG StartPointer = (ULONG)pwchBuffer;
|
|
|
|
if ((Adapter->InterfaceType != Isa) && (Adapter->InterfaceType != Eisa))
|
|
{
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// First put in memory addr
|
|
//
|
|
|
|
//
|
|
// Copy in the title string
|
|
//
|
|
CopyLength = UnicodeStrLen(MemAddrString) + 1;
|
|
if (OutputLengthLeft < CopyLength)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
RtlMoveMemory((PVOID)pwchBuffer,
|
|
(PVOID)MemAddrString,
|
|
(CopyLength * sizeof(WCHAR)));
|
|
|
|
pwchBuffer = &(pwchBuffer[CopyLength]);
|
|
OutputLengthLeft -= CopyLength;
|
|
|
|
//
|
|
// Copy in the value
|
|
//
|
|
if (OutputLengthLeft < 8)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
CopyLength = wsprintf(pwchBuffer,L"0x%x", Adapter->MemoryAddress);
|
|
if (CopyLength < 0)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
CopyLength++; // Add in the \0
|
|
|
|
pwchBuffer = &(pwchBuffer[CopyLength]);
|
|
OutputLengthLeft -= CopyLength;
|
|
|
|
//
|
|
// Now the amount of memory
|
|
//
|
|
|
|
//
|
|
// Copy in the title string
|
|
//
|
|
CopyLength = UnicodeStrLen(MemLengthString) + 1;
|
|
if (OutputLengthLeft < CopyLength)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
RtlMoveMemory((PVOID)pwchBuffer,
|
|
(PVOID)MemLengthString,
|
|
(CopyLength * sizeof(WCHAR)));
|
|
|
|
pwchBuffer = &(pwchBuffer[CopyLength]);
|
|
OutputLengthLeft -= CopyLength;
|
|
|
|
//
|
|
// Copy in the value
|
|
//
|
|
if (OutputLengthLeft < 8)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
CopyLength = wsprintf(pwchBuffer,L"0x%x", Adapter->MemoryLength);
|
|
|
|
if (CopyLength < 0)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
CopyLength++; // Add in the \0
|
|
|
|
pwchBuffer = &(pwchBuffer[CopyLength]);
|
|
OutputLengthLeft -= CopyLength;
|
|
|
|
//
|
|
// Now the IoBaseAddress
|
|
//
|
|
|
|
//
|
|
// Copy in the title string
|
|
//
|
|
CopyLength = UnicodeStrLen(IoAddrString) + 1;
|
|
|
|
if (OutputLengthLeft < CopyLength)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
RtlMoveMemory((PVOID)pwchBuffer,
|
|
(PVOID)IoAddrString,
|
|
(CopyLength * sizeof(WCHAR)));
|
|
|
|
pwchBuffer = &(pwchBuffer[CopyLength]);
|
|
OutputLengthLeft -= CopyLength;
|
|
|
|
//
|
|
// Copy in the value
|
|
//
|
|
if (OutputLengthLeft < 6)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
CopyLength = wsprintf(pwchBuffer,L"0x%x", ((Adapter->IoBaseAddress == PRIMARY) ? 1 : 2));
|
|
if (CopyLength < 0)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
CopyLength++; // Add in the \0
|
|
|
|
pwchBuffer = &(pwchBuffer[CopyLength]);
|
|
OutputLengthLeft -= CopyLength;
|
|
|
|
//
|
|
// Now the IoAddressLength
|
|
//
|
|
|
|
//
|
|
// Copy in the title string
|
|
//
|
|
CopyLength = UnicodeStrLen(IoLengthString) + 1;
|
|
|
|
if (OutputLengthLeft < CopyLength)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
RtlMoveMemory((PVOID)pwchBuffer,
|
|
(PVOID)IoLengthString,
|
|
(CopyLength * sizeof(WCHAR)));
|
|
|
|
pwchBuffer = &(pwchBuffer[CopyLength]);
|
|
OutputLengthLeft -= CopyLength;
|
|
|
|
//
|
|
// Copy in the value
|
|
//
|
|
if (OutputLengthLeft < 4)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
CopyLength = wsprintf(pwchBuffer,L"0x4");
|
|
if (CopyLength < 0)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
CopyLength++; // Add in the \0
|
|
|
|
pwchBuffer = &(pwchBuffer[CopyLength]);
|
|
OutputLengthLeft -= CopyLength;
|
|
|
|
//
|
|
// Copy in the title string
|
|
//
|
|
CopyLength = UnicodeStrLen(IrqString) + 1;
|
|
|
|
if (OutputLengthLeft < CopyLength)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
RtlMoveMemory((PVOID)pwchBuffer,
|
|
(PVOID)IrqString,
|
|
(CopyLength * sizeof(WCHAR)));
|
|
|
|
pwchBuffer = &(pwchBuffer[CopyLength]);
|
|
OutputLengthLeft -= CopyLength;
|
|
|
|
//
|
|
// Copy in the value
|
|
//
|
|
if (OutputLengthLeft < 3)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
CopyLength = wsprintf(pwchBuffer,L"%d", Adapter->Interrupt);
|
|
|
|
if (CopyLength < 0)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
CopyLength++; // Add in the \0
|
|
|
|
pwchBuffer = &(pwchBuffer[CopyLength]);
|
|
OutputLengthLeft -= CopyLength;
|
|
|
|
//
|
|
// Copy in the title string (IRQTYPE)
|
|
//
|
|
CopyLength = UnicodeStrLen(IrqTypeString) + 1;
|
|
|
|
if (OutputLengthLeft < CopyLength)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
RtlMoveMemory((PVOID)pwchBuffer,
|
|
(PVOID)IrqTypeString,
|
|
(CopyLength * sizeof(WCHAR)));
|
|
|
|
pwchBuffer = &(pwchBuffer[CopyLength]);
|
|
OutputLengthLeft -= CopyLength;
|
|
|
|
//
|
|
// Copy in the value
|
|
//
|
|
if (OutputLengthLeft < 2)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
//
|
|
// All cards in this detection code are ISA cards, which
|
|
// are LATCHED (0 == latched)
|
|
//
|
|
CopyLength = wsprintf(pwchBuffer,L"0");
|
|
|
|
if (CopyLength < 0)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
CopyLength++; // Add in the \0
|
|
|
|
pwchBuffer = &(pwchBuffer[CopyLength]);
|
|
OutputLengthLeft -= CopyLength;
|
|
|
|
//
|
|
// Copy in final \0
|
|
//
|
|
if (OutputLengthLeft < 1)
|
|
{
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
CopyLength = (ULONG)pwchBuffer - StartPointer;
|
|
((PUCHAR)StartPointer)[CopyLength] = L'\0';
|
|
|
|
return(0);
|
|
}
|
|
|
|
extern
|
|
LONG
|
|
IbmtokVerifyCfgHandler(
|
|
IN PVOID pvHandle,
|
|
IN WCHAR *pwchBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine verifys that a given parameter list is complete and
|
|
correct for the adapter associated with the handle.
|
|
|
|
Arguments:
|
|
|
|
pvHandle - The handle.
|
|
|
|
pwchBuffer - The parameter list.
|
|
|
|
Return Value:
|
|
|
|
0 if nothing went wrong, else the appropriate WINERROR.H value.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIBMTOK_ADAPTER Adapter = (PIBMTOK_ADAPTER)(pvHandle);
|
|
ULONG IoBaseAddress;
|
|
WCHAR *Place;
|
|
BOOLEAN Found;
|
|
NETDTECT_RESOURCE Resource;
|
|
|
|
if ((Adapter->InterfaceType != Isa) && (Adapter->InterfaceType != Eisa))
|
|
{
|
|
return(ERROR_INVALID_DATA);
|
|
}
|
|
|
|
if (Adapter->CardType == 1000)
|
|
{
|
|
//
|
|
// Parse out the parameter.
|
|
//
|
|
|
|
//
|
|
// Get the IoBaseAddress
|
|
//
|
|
Place = FindParameterString(pwchBuffer, IoAddrString);
|
|
if (Place == NULL)
|
|
{
|
|
return(ERROR_INVALID_DATA);
|
|
}
|
|
|
|
Place += UnicodeStrLen(IoAddrString) + 1;
|
|
|
|
//
|
|
// Now parse the thing.
|
|
//
|
|
ScanForNumber(Place, &IoBaseAddress, &Found);
|
|
if (Found == FALSE)
|
|
{
|
|
return(ERROR_INVALID_DATA);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Error!
|
|
//
|
|
return(ERROR_INVALID_DATA);
|
|
}
|
|
|
|
//
|
|
// Verify IoAddress
|
|
//
|
|
if (IoBaseAddress != ((Adapter->IoBaseAddress == PRIMARY) ? (ULONG)1 : (ULONG)2))
|
|
{
|
|
UCHAR Interrupt;
|
|
ULONG MemoryAddress;
|
|
ULONG MemoryLength;
|
|
|
|
if (!IbmtokCardAt(Adapter->InterfaceType,
|
|
Adapter->BusNumber,
|
|
(IoBaseAddress == 1) ? PRIMARY : SECONDARY,
|
|
&Interrupt,
|
|
&MemoryAddress,
|
|
&MemoryLength))
|
|
{
|
|
DBGPRINT(("IbmtokVerifyCfgHandler: Unable to find adapter!\n"));
|
|
return(ERROR_INVALID_DATA);
|
|
}
|
|
|
|
//
|
|
// Release the old io address and acquire the new one.
|
|
//
|
|
Resource.InterfaceType = Adapter->InterfaceType;
|
|
Resource.BusNumber = Adapter->BusNumber;
|
|
Resource.Type = NETDTECT_PORT_RESOURCE;
|
|
Resource.Value = Adapter->IoBaseAddress;
|
|
Resource.Length = 0x4;
|
|
Resource.Flags = 0;
|
|
|
|
DetectFreeSpecificTemporaryResource(&Resource);
|
|
|
|
Resource.Value = (IoBaseAddress == 1) ? PRIMARY : SECONDARY;
|
|
Adapter->IoBaseAddress = Resource.Value;
|
|
|
|
DetectTemporaryClaimResource(&Resource);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
extern
|
|
LONG IbmtokQueryMaskHandler(
|
|
IN LONG lNetcardId,
|
|
OUT WCHAR *pwchBuffer,
|
|
IN LONG cwchBuffSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the parameter list information for a specific
|
|
network card.
|
|
|
|
Arguments:
|
|
|
|
lNetcardId - The id of the desired netcard.
|
|
|
|
pwchBuffer - The buffer for storing the parameter information.
|
|
|
|
cwchBuffSize - Length of pwchBuffer in WCHARs.
|
|
|
|
Return Value:
|
|
|
|
0 if nothing went wrong, else the appropriate WINERROR.H value.
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR *Result;
|
|
LONG Length;
|
|
LONG NumberOfAdapters;
|
|
LONG i;
|
|
|
|
//
|
|
// Find the adapter
|
|
//
|
|
|
|
NumberOfAdapters = sizeof(Adapters) / sizeof(ADAPTER_INFO);
|
|
|
|
for (i=0; i < NumberOfAdapters; i++) {
|
|
|
|
if (Adapters[i].Index == lNetcardId) {
|
|
|
|
Result = Adapters[i].Parameters;
|
|
|
|
//
|
|
// Find the string length (Ends with 2 NULLs)
|
|
//
|
|
|
|
for (Length=0; ; Length++) {
|
|
|
|
if (Result[Length] == L'\0') {
|
|
|
|
++Length;
|
|
|
|
if (Result[Length] == L'\0') {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Length++;
|
|
|
|
if (cwchBuffSize < Length) {
|
|
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
}
|
|
|
|
memcpy((PVOID)pwchBuffer, Result, Length * sizeof(WCHAR));
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(ERROR_INVALID_PARAMETER);
|
|
|
|
}
|
|
|
|
extern
|
|
LONG
|
|
IbmtokParamRangeHandler(
|
|
IN LONG lNetcardId,
|
|
IN WCHAR *pwchParam,
|
|
OUT LONG *plValues,
|
|
OUT LONG *plBuffSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a list of valid values for a given parameter name
|
|
for a given card.
|
|
|
|
Arguments:
|
|
|
|
lNetcardId - The Id of the card desired.
|
|
|
|
pwchParam - A WCHAR string of the parameter name to query the values of.
|
|
|
|
plValues - A pointer to a list of LONGs into which we store valid values
|
|
for the parameter.
|
|
|
|
plBuffSize - At entry, the length of plValues in LONGs. At exit, the
|
|
number of LONGs stored in plValues.
|
|
|
|
Return Value:
|
|
|
|
0 if nothing went wrong, else the appropriate WINERROR.H value.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// Do we want the IoBaseAddress
|
|
//
|
|
|
|
if (memcmp(pwchParam, IoAddrString, (UnicodeStrLen(IoAddrString) + 1) * sizeof(WCHAR)) == 0) {
|
|
|
|
//
|
|
// Is there enough space
|
|
//
|
|
|
|
if (*plBuffSize < 2) {
|
|
|
|
*plBuffSize = 0;
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
|
|
}
|
|
|
|
plValues[0] = 1;
|
|
plValues[1] = 2;
|
|
*plBuffSize = 2;
|
|
return(0);
|
|
|
|
}
|
|
|
|
return(ERROR_INVALID_PARAMETER);
|
|
|
|
}
|
|
|
|
extern
|
|
LONG IbmtokQueryParameterNameHandler(
|
|
IN WCHAR *pwchParam,
|
|
OUT WCHAR *pwchBuffer,
|
|
IN LONG cwchBufferSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a localized, displayable name for a specific parameter. All the
|
|
parameters that this file uses are define by MS, so no strings are
|
|
needed here.
|
|
|
|
Arguments:
|
|
|
|
pwchParam - The parameter to be queried.
|
|
|
|
pwchBuffer - The buffer to store the result into.
|
|
|
|
cwchBufferSize - The length of pwchBuffer in WCHARs.
|
|
|
|
Return Value:
|
|
|
|
ERROR_INVALID_PARAMETER -- To indicate that the MS supplied strings
|
|
should be used.
|
|
|
|
--*/
|
|
|
|
{
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
BOOLEAN
|
|
IbmtokCardAt(
|
|
IN INTERFACE_TYPE InterfaceType,
|
|
IN ULONG BusNumber,
|
|
IN ULONG IoBaseAddress,
|
|
OUT PUCHAR Interrupt,
|
|
OUT PULONG MemoryAddress,
|
|
OUT PULONG MemoryLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks for the instance of a Ibmtok card at the Io
|
|
location given. This is done by checking for the card signature
|
|
in memory. The memory location is found by reading the ports.
|
|
|
|
Arguments:
|
|
|
|
InterfaceType - The type of bus, ISA or EISA.
|
|
|
|
BusNumber - The bus number in the system.
|
|
|
|
IoBaseAddress - The IO port address of the card.
|
|
|
|
Return Value:
|
|
|
|
TRUE if a card is found, else FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS NtStatus;
|
|
|
|
//
|
|
// Holds the value read from the SWITCH_READ_1 port.
|
|
//
|
|
UCHAR sfi; // Note: SUPPORTED_FUNCTION_IDENTIFIERS 0x1FA0
|
|
|
|
//
|
|
// Holds the physical address of the MMIO region.
|
|
//
|
|
ULONG MmioAddress;
|
|
|
|
//
|
|
// Will hold the Adapter ID as read from the card.
|
|
//
|
|
ULONG AdapterId[3];
|
|
|
|
//
|
|
// What AdapterId should contain for a PC I/O bus card.
|
|
//
|
|
static ULONG PcIoBusId[3] = { 0x5049434f, 0x36313130, 0x39393020 };
|
|
|
|
//
|
|
// Loop counters.
|
|
//
|
|
UINT i, j;
|
|
|
|
// The values in memory
|
|
//
|
|
UCHAR Memory[48];
|
|
|
|
UCHAR InterruptNumber;
|
|
NETDTECT_RESOURCE Resource;
|
|
UCHAR SwitchRead1;
|
|
UCHAR BoundaryNeeded;
|
|
UCHAR TempAddress;
|
|
UCHAR SharedRamBits;
|
|
ULONG SharedRamSize;
|
|
ULONG MemAddress;
|
|
ULONG MemLength;
|
|
|
|
//
|
|
// Check for resource conflict
|
|
//
|
|
NtStatus = DetectCheckPortUsage(InterfaceType,
|
|
BusNumber,
|
|
IoBaseAddress,
|
|
0x4);
|
|
if (NtStatus != STATUS_SUCCESS)
|
|
{
|
|
DBGPRINT(("IbmtokCardAt: Looks like IoBaseAddress 0x%x is already in use.\n"));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// SwitchRead1 contains the interrupt code in the low 2 bits,
|
|
// and bits 18 through 13 of the MMIO address in the high
|
|
// 6 bits.
|
|
//
|
|
NtStatus = DetectReadPortUchar(InterfaceType,
|
|
BusNumber,
|
|
IoBaseAddress,
|
|
&SwitchRead1);
|
|
if (NtStatus != STATUS_SUCCESS)
|
|
{
|
|
DBGPRINT(("IbmtokCardAt: Unable to read SwitchRead1\n"));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// To compute MmioAddress, we mask off the low 2 bits of
|
|
// SwitchRead1, shift it out by 11 (so that the high 6 bits
|
|
// are moved to the right place), and add in the 19th bit value.
|
|
//
|
|
MmioAddress = ((SwitchRead1 & 0xfc) << 11) | (1 << 19);
|
|
|
|
if ((((MmioAddress & 0xF0000) != 0xC0000) &&
|
|
((MmioAddress & 0xF0000) != 0xD0000)) ||
|
|
|
|
(((MmioAddress & 0x0F000) != 0x00000) &&
|
|
((MmioAddress & 0x0F000) != 0x02000) &&
|
|
((MmioAddress & 0x0F000) != 0x04000) &&
|
|
((MmioAddress & 0x0F000) != 0x06000) &&
|
|
((MmioAddress & 0x0F000) != 0x08000) &&
|
|
((MmioAddress & 0x0F000) != 0x0A000) &&
|
|
((MmioAddress & 0x0F000) != 0x0C000) &&
|
|
((MmioAddress & 0x0F000) != 0x0E000)) ||
|
|
|
|
((MmioAddress & 0x00FFF) != 0x0))
|
|
{
|
|
//
|
|
// Definitely NOT!
|
|
//
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Now we have mapped the MMIO, look at the AIP. First
|
|
// determine the channel identifier.
|
|
//
|
|
|
|
// Note: CHANNEL_IDENTIFIER 0x1f30
|
|
|
|
NtStatus = DetectReadMappedMemory(InterfaceType,
|
|
BusNumber,
|
|
MmioAddress + 0x1F30,
|
|
48,
|
|
Memory);
|
|
if (NtStatus != STATUS_SUCCESS)
|
|
{
|
|
DBGPRINT(("IbmtokCardAt: Unable to read mapped memory\n"));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Read in AdapterId.
|
|
//
|
|
// Turns out that the bytes which identify the card are stored
|
|
// in a very odd manner. There are 48 bytes on the card. The
|
|
// even numbered bytes contain 4 bits of the card signature.
|
|
//
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
AdapterId[i] = 0;
|
|
|
|
for (j = 0; j < 16; j += 2)
|
|
{
|
|
AdapterId[i] = (AdapterId[i] << 4) + Memory[(i * 16 + j)];
|
|
}
|
|
}
|
|
|
|
if ((AdapterId[0] != PcIoBusId[0]) ||
|
|
(AdapterId[1] != PcIoBusId[1]) ||
|
|
(AdapterId[2] != PcIoBusId[2]))
|
|
{
|
|
DBGPRINT(("IbmtokCardAt: Invalid AdapterId\n"));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Old, non-auto16/4 ISA cards will have
|
|
// Support Function Ids values greater than 0x0C.
|
|
// Any new cards will (such as the Auto16/4 card)
|
|
// will have value less or equal to 0x0C.
|
|
//
|
|
NtStatus = DetectReadMappedMemory(InterfaceType,
|
|
BusNumber,
|
|
MmioAddress + 0x1FA0,
|
|
1,
|
|
&sfi);
|
|
if (NtStatus != STATUS_SUCCESS)
|
|
{
|
|
DBGPRINT(("IbmtokCardAt: Failed read the Support Function Id to determine card type\n"));
|
|
return(FALSE);
|
|
}
|
|
|
|
if (sfi <= 0x0C)
|
|
{
|
|
//
|
|
// a sfi value > 0x0C indicates old ISA TR
|
|
//
|
|
DBGPRINT(("IbmtokCardAt: Invalid Support Function Id 0x%x\n", sfi));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Acquire the io port
|
|
//
|
|
Resource.InterfaceType = InterfaceType;
|
|
Resource.BusNumber = BusNumber;
|
|
Resource.Type = NETDTECT_PORT_RESOURCE;
|
|
Resource.Value = IoBaseAddress;
|
|
Resource.Length = 0x4;
|
|
Resource.Flags = 0;
|
|
|
|
DetectTemporaryClaimResource(&Resource);
|
|
|
|
//
|
|
// Now get the IRQ
|
|
//
|
|
|
|
//
|
|
// SwitchRead1 contains the interrupt code in the low 2 bits,
|
|
// and bits 18 through 13 of the MMIO address in the high
|
|
// 6 bits.
|
|
//
|
|
NtStatus = DetectReadPortUchar(
|
|
InterfaceType,
|
|
BusNumber,
|
|
IoBaseAddress,
|
|
&SwitchRead1);
|
|
if (NtStatus != STATUS_SUCCESS)
|
|
{
|
|
DBGPRINT(("Failed to read the SwitchRead1 (second time)\n"));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the interrupt level...note that a switch being
|
|
// "off" shows up as a 1, "on" is 0.
|
|
//
|
|
switch (SwitchRead1 & 0x03)
|
|
{
|
|
case 0: InterruptNumber = 2; break;
|
|
case 1: InterruptNumber = 3; break;
|
|
case 2: InterruptNumber = 6; break;
|
|
case 3: InterruptNumber = 7; break;
|
|
}
|
|
|
|
//
|
|
// Acquire the interrupt.
|
|
//
|
|
Resource.Type = NETDTECT_IRQ_RESOURCE;
|
|
Resource.Value = InterruptNumber;
|
|
Resource.Length = 0;
|
|
Resource.Flags = 0;
|
|
|
|
DetectTemporaryClaimResource(&Resource);
|
|
|
|
*Interrupt = InterruptNumber;
|
|
|
|
//
|
|
// Now get the MemoryMappedBaseAddress
|
|
//
|
|
|
|
//
|
|
// To compute MmioAddress, we mask off the low 2 bits of
|
|
// SwitchRead1, shift it out by 11 (so that the high 6 bits
|
|
// are moved to the right place), and add in the 19th bit value.
|
|
//
|
|
TempAddress = ((((SwitchRead1 & 0xfc) >> 1) | 0x80) + 0x02);
|
|
MemAddress = ((SwitchRead1 & 0xfc) << 11) | (1 << 19);
|
|
|
|
// NOTE: RRR_HIGH 0x1e01
|
|
|
|
NtStatus = DetectReadMappedMemory(InterfaceType,
|
|
BusNumber,
|
|
MemAddress + 0x1E01,
|
|
1,
|
|
&SharedRamBits);
|
|
//
|
|
// Get memory size
|
|
//
|
|
SharedRamBits = ((SharedRamBits & 0x0c) >> 2);
|
|
|
|
switch (SharedRamBits)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
|
|
//
|
|
// 8K or 16K needs a 16K boundary.
|
|
//
|
|
SharedRamSize = (SharedRamBits == 0) ? 0x2000 : 0x4000;
|
|
BoundaryNeeded = 0x04;
|
|
break;
|
|
|
|
case 2:
|
|
|
|
//
|
|
// 32K needs a 32K boundary.
|
|
//
|
|
SharedRamSize = 0x8000;
|
|
BoundaryNeeded = 0x08;
|
|
break;
|
|
|
|
case 3:
|
|
|
|
//
|
|
// 64K needs a 64K boundary.
|
|
//
|
|
SharedRamSize = 0x10000;
|
|
BoundaryNeeded = 0x10;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If TempAddress is not on the proper boundary, move it
|
|
// forward until it is.
|
|
//
|
|
if (TempAddress & (BoundaryNeeded-1))
|
|
{
|
|
TempAddress = (UCHAR)((TempAddress & ~(BoundaryNeeded-1)) + BoundaryNeeded);
|
|
}
|
|
|
|
//
|
|
// Compute the total length
|
|
//
|
|
MemLength = (((ULONG)TempAddress) << 12) - MemAddress;
|
|
MemLength += SharedRamSize;
|
|
|
|
//
|
|
// Acquire the memory range.
|
|
//
|
|
Resource.Type = NETDTECT_MEMORY_RESOURCE;
|
|
Resource.Value = MemAddress;
|
|
Resource.Length = MemLength;
|
|
Resource.Flags = 0;
|
|
|
|
DetectTemporaryClaimResource(&Resource);
|
|
|
|
*MemoryAddress = MemAddress;
|
|
*MemoryLength = MemLength;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|