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.
893 lines
18 KiB
893 lines
18 KiB
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cxport.c
|
|
|
|
Abstract:
|
|
|
|
Common Transport Environment utility functions for the NT environment
|
|
|
|
Author:
|
|
|
|
Mike Massa (mikemas) Aug 11, 1993
|
|
|
|
Revision History:
|
|
|
|
Who When What
|
|
-------- -------- ----------------------------------------------
|
|
mikemas 08-11-93 created
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4115) // named type definition in parentheses ntddk.h
|
|
#include <ntddk.h>
|
|
#pragma warning(pop)
|
|
|
|
#include <ndis.h>
|
|
#include <cxport.h>
|
|
#include <tdistat.h>
|
|
|
|
|
|
//
|
|
// Mark pageable code
|
|
//
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text(PAGE, CTELogEvent)
|
|
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
|
|
//
|
|
// Local variables
|
|
//
|
|
ULONG CTEpTimeIncrement = 0; // used to convert kernel clock ticks to 100ns.
|
|
LIST_ENTRY CTEBlockListHead;
|
|
KSPIN_LOCK CTEBlockSpinLock;
|
|
|
|
// Used in the conversion of 100ns times to milliseconds.
|
|
static LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758};
|
|
|
|
|
|
|
|
//
|
|
// Macros
|
|
//
|
|
//++
|
|
//
|
|
// LARGE_INTEGER
|
|
// CTEConvertMillisecondsTo100ns(
|
|
// IN LARGE_INTEGER MsTime
|
|
// );
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// Converts time expressed in hundreds of nanoseconds to milliseconds.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// MsTime - Time in milliseconds.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Time in hundreds of nanoseconds.
|
|
//
|
|
//--
|
|
|
|
#define CTEConvertMillisecondsTo100ns(MsTime) \
|
|
RtlExtendedIntegerMultiply(MsTime, 10000)
|
|
|
|
|
|
//++
|
|
//
|
|
// LARGE_INTEGER
|
|
// CTEConvert100nsToMilliseconds(
|
|
// IN LARGE_INTEGER HnsTime
|
|
// );
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// Converts time expressed in hundreds of nanoseconds to milliseconds.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// HnsTime - Time in hundreds of nanoseconds.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Time in milliseconds.
|
|
//
|
|
//--
|
|
|
|
#define SHIFT10000 13
|
|
extern LARGE_INTEGER Magic10000;
|
|
|
|
#define CTEConvert100nsToMilliseconds(HnsTime) \
|
|
RtlExtendedMagicDivide((HnsTime), Magic10000, SHIFT10000)
|
|
|
|
|
|
//
|
|
// Local functions
|
|
//
|
|
VOID
|
|
CTEpInitialize(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes internal module state.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
CTEpTimeIncrement = KeQueryTimeIncrement();
|
|
KeInitializeSpinLock(&CTEBlockSpinLock);
|
|
InitializeListHead(&CTEBlockListHead);
|
|
}
|
|
|
|
VOID
|
|
CTEpEventHandler(
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Internal handler for scheduled CTE Events. Conforms to calling convention
|
|
for ExWorkerThread handlers. Calls the CTE handler registered for this
|
|
event.
|
|
|
|
Arguments:
|
|
|
|
Context - Work item to process.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
CTEEvent *Event;
|
|
CTELockHandle Handle;
|
|
CTEEventRtn Handler;
|
|
PVOID Arg;
|
|
|
|
|
|
#if DBG
|
|
KIRQL StartingIrql;
|
|
|
|
StartingIrql = KeGetCurrentIrql();
|
|
#endif
|
|
|
|
Event = (CTEEvent *) Context;
|
|
|
|
CTEGetLock(&(Event->ce_lock), &Handle);
|
|
ASSERT(Event->ce_scheduled);
|
|
Handler = Event->ce_handler;
|
|
Arg = Event->ce_arg;
|
|
Event->ce_scheduled = 0;
|
|
CTEFreeLock(&(Event->ce_lock), Handle);
|
|
|
|
Handler(Event, Arg);
|
|
|
|
#if DBG
|
|
if (KeGetCurrentIrql() != StartingIrql) {
|
|
DbgPrint(
|
|
"CTEpEventHandler: routine %lx , event %lx returned at raised IRQL\n",
|
|
Handler, Event
|
|
);
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
VOID
|
|
CTEpTimerHandler(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Internal handler for scheduled CTE Timers. Conforms to calling convention
|
|
for NT DPC handlers. Calls the CTE handler registered for this timer.
|
|
|
|
Arguments:
|
|
|
|
Dpc - Pointer to the DPC routine being run.
|
|
DeferredContext - Private context for this instance of the DPC.
|
|
SystemArgument1 - Additional argument.
|
|
SystemArgument2 - Additional argument.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
CTETimer *Timer;
|
|
|
|
UNREFERENCED_PARAMETER(Dpc);
|
|
UNREFERENCED_PARAMETER(SystemArgument1);
|
|
UNREFERENCED_PARAMETER(SystemArgument2);
|
|
|
|
Timer = (CTETimer *) DeferredContext;
|
|
(*Timer->t_handler)((CTEEvent *)Timer, Timer->t_arg);
|
|
}
|
|
|
|
|
|
//
|
|
// Exported functions.
|
|
//
|
|
int
|
|
CTEInitialize(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
An empty initialization routine for backward compatibility.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Non-zero.
|
|
|
|
--*/
|
|
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
CTEInitEvent(
|
|
CTEEvent *Event,
|
|
CTEEventRtn Handler
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes a CTE Event variable.
|
|
|
|
Arguments:
|
|
|
|
Event - Event variable to initialize.
|
|
Handler - Handler routine for this event variable.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT(Handler != NULL);
|
|
|
|
Event->ce_handler = Handler;
|
|
Event->ce_scheduled = 0;
|
|
CTEInitLock(&(Event->ce_lock));
|
|
ExInitializeWorkItem(&(Event->ce_workitem), CTEpEventHandler, Event);
|
|
}
|
|
|
|
|
|
int
|
|
CTEScheduleCriticalEvent(
|
|
IN CTEEvent *Event,
|
|
IN void *Argument OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Schedules a routine to be executed later in a different context. In the
|
|
NT environment, the event is implemented using Executive worker threads.
|
|
|
|
Arguments:
|
|
|
|
Event - Pointer to a CTE Event variable
|
|
Argument - An argument to pass to the event handler when it is called
|
|
|
|
Return Value:
|
|
|
|
0 if the event could not be scheduled. Nonzero otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
CTELockHandle Handle;
|
|
|
|
CTEGetLock(&(Event->ce_lock), &Handle);
|
|
|
|
if (!(Event->ce_scheduled)) {
|
|
Event->ce_scheduled = 1;
|
|
Event->ce_arg = Argument;
|
|
|
|
ExQueueWorkItem(
|
|
&(Event->ce_workitem),
|
|
CriticalWorkQueue
|
|
);
|
|
}
|
|
|
|
CTEFreeLock(&(Event->ce_lock), Handle);
|
|
|
|
return(1);
|
|
}
|
|
|
|
|
|
int
|
|
CTEScheduleEvent(
|
|
IN CTEEvent *Event,
|
|
IN void *Argument OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Schedules a routine to be executed later in a different context. In the
|
|
NT environment, the event is implemented using Executive worker threads.
|
|
|
|
Arguments:
|
|
|
|
Event - Pointer to a CTE Event variable
|
|
Argument - An argument to pass to the event handler when it is called
|
|
|
|
Return Value:
|
|
|
|
0 if the event could not be scheduled. Nonzero otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
CTELockHandle Handle;
|
|
|
|
CTEGetLock(&(Event->ce_lock), &Handle);
|
|
|
|
if (!(Event->ce_scheduled)) {
|
|
Event->ce_scheduled = 1;
|
|
Event->ce_arg = Argument;
|
|
|
|
ExQueueWorkItem(
|
|
&(Event->ce_workitem),
|
|
DelayedWorkQueue
|
|
);
|
|
}
|
|
|
|
CTEFreeLock(&(Event->ce_lock), Handle);
|
|
|
|
return(1);
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
CTEScheduleDelayedEvent(
|
|
IN CTEEvent *Event,
|
|
IN void *Argument OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Schedules a routine to be executed later in a different context. In the
|
|
NT environment, the event is implemented using Executive worker threads.
|
|
|
|
Arguments:
|
|
|
|
Event - Pointer to a CTE Event variable
|
|
Argument - An argument to pass to the event handler when it is called
|
|
|
|
Return Value:
|
|
|
|
0 if the event could not be scheduled. Nonzero otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
CTELockHandle Handle;
|
|
|
|
CTEGetLock(&(Event->ce_lock), &Handle);
|
|
|
|
if (!(Event->ce_scheduled)) {
|
|
Event->ce_scheduled = 1;
|
|
Event->ce_arg = Argument;
|
|
|
|
ExQueueWorkItem(
|
|
&(Event->ce_workitem),
|
|
DelayedWorkQueue
|
|
);
|
|
}
|
|
|
|
CTEFreeLock(&(Event->ce_lock), Handle);
|
|
|
|
return(1);
|
|
}
|
|
|
|
void
|
|
CTEInitTimer(
|
|
CTETimer *Timer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes a CTE Timer variable.
|
|
|
|
Arguments:
|
|
|
|
Timer - Timer variable to initialize.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
Timer->t_handler = NULL;
|
|
Timer->t_arg = NULL;
|
|
KeInitializeDpc(&(Timer->t_dpc), CTEpTimerHandler, Timer);
|
|
KeInitializeTimer(&(Timer->t_timer));
|
|
}
|
|
|
|
|
|
void *
|
|
CTEStartTimer(
|
|
CTETimer *Timer,
|
|
unsigned long DueTime,
|
|
CTEEventRtn Handler,
|
|
void *Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets a CTE Timer for expiration.
|
|
|
|
Arguments:
|
|
|
|
Timer - Pointer to a CTE Timer variable.
|
|
DueTime - Time in milliseconds after which the timer should expire.
|
|
Handler - Timer expiration handler routine.
|
|
Context - Argument to pass to the handler.
|
|
|
|
Return Value:
|
|
|
|
0 if the timer could not be set. Nonzero otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER LargeDueTime;
|
|
|
|
ASSERT(Handler != NULL);
|
|
|
|
//
|
|
// Convert milliseconds to hundreds of nanoseconds and negate to make
|
|
// an NT relative timeout.
|
|
//
|
|
LargeDueTime.HighPart = 0;
|
|
LargeDueTime.LowPart = DueTime;
|
|
LargeDueTime = CTEConvertMillisecondsTo100ns(LargeDueTime);
|
|
LargeDueTime.QuadPart = -LargeDueTime.QuadPart;
|
|
|
|
Timer->t_handler = Handler;
|
|
Timer->t_arg = Context;
|
|
|
|
KeSetTimer(
|
|
&(Timer->t_timer),
|
|
LargeDueTime,
|
|
&(Timer->t_dpc)
|
|
);
|
|
|
|
return((void *) 1);
|
|
}
|
|
|
|
|
|
unsigned long
|
|
CTESystemUpTime(
|
|
void
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Provides the time since system boot in milliseconds.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
The time since boot in milliseconds.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER TickCount;
|
|
|
|
//
|
|
// Get tick count and convert to hundreds of nanoseconds.
|
|
//
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4127) // condition expression constant
|
|
|
|
KeQueryTickCount(&TickCount);
|
|
|
|
#pragma warning(pop)
|
|
|
|
TickCount = RtlExtendedIntegerMultiply(
|
|
TickCount,
|
|
(LONG) CTEpTimeIncrement
|
|
);
|
|
|
|
TickCount = CTEConvert100nsToMilliseconds(TickCount);
|
|
|
|
return(TickCount.LowPart);
|
|
}
|
|
|
|
|
|
uint
|
|
CTEBlock(
|
|
IN CTEBlockStruc *BlockEvent
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = KeWaitForSingleObject(
|
|
&(BlockEvent->cbs_event),
|
|
UserRequest,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
BlockEvent->cbs_status = Status;
|
|
}
|
|
|
|
return(BlockEvent->cbs_status);
|
|
}
|
|
|
|
uint
|
|
CTEBlockWithTracker(
|
|
IN CTEBlockStruc *BlockEvent,
|
|
IN CTEBlockTracker *BlockTracker,
|
|
IN void *Context
|
|
)
|
|
{
|
|
uint Status;
|
|
|
|
CTEInsertBlockTracker(BlockTracker, Context);
|
|
Status = CTEBlock(BlockEvent);
|
|
CTERemoveBlockTracker(BlockTracker);
|
|
|
|
return Status;
|
|
}
|
|
|
|
void
|
|
CTEInsertBlockTracker(
|
|
IN CTEBlockTracker *BlockTracker,
|
|
IN void *Context
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
BlockTracker->cbt_thread = KeGetCurrentThread();
|
|
BlockTracker->cbt_context = Context;
|
|
|
|
KeAcquireSpinLock(&CTEBlockSpinLock, &OldIrql);
|
|
InsertTailList(&CTEBlockListHead, &BlockTracker->cbt_link);
|
|
KeReleaseSpinLock(&CTEBlockSpinLock, OldIrql);
|
|
}
|
|
|
|
void
|
|
CTERemoveBlockTracker(
|
|
IN CTEBlockTracker *BlockTracker
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
KeAcquireSpinLock(&CTEBlockSpinLock, &OldIrql);
|
|
RemoveEntryList(&BlockTracker->cbt_link);
|
|
KeReleaseSpinLock(&CTEBlockSpinLock, OldIrql);
|
|
}
|
|
|
|
void
|
|
CTESignal(
|
|
IN CTEBlockStruc *BlockEvent,
|
|
IN uint Status
|
|
)
|
|
{
|
|
BlockEvent->cbs_status = Status;
|
|
KeSetEvent(&(BlockEvent->cbs_event), 0, FALSE);
|
|
return;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CTEInitString(
|
|
IN OUT PNDIS_STRING DestinationString,
|
|
IN char *SourceString
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts a C style ASCII string to an NDIS_STRING. Resources needed for
|
|
the NDIS_STRING are allocated and must be freed by a call to
|
|
CTEFreeString.
|
|
|
|
Arguments:
|
|
|
|
DestinationString - A pointer to an NDIS_STRING variable with no
|
|
associated data buffer.
|
|
|
|
SourceString - The C style ASCII string source.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if the initialization succeeded. FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
STRING AnsiString;
|
|
ULONG UnicodeLength;
|
|
|
|
RtlInitString(&AnsiString, SourceString);
|
|
|
|
// calculate size of unicoded ansi string + 2 for NULL terminator
|
|
UnicodeLength = RtlAnsiStringToUnicodeSize(&AnsiString) + 2;
|
|
|
|
// allocate storage for the unicode string
|
|
DestinationString->Buffer = ExAllocatePool(NonPagedPool, UnicodeLength);
|
|
|
|
if (DestinationString->Buffer == NULL) {
|
|
DestinationString->MaximumLength = 0;
|
|
return(FALSE);
|
|
}
|
|
|
|
DestinationString->MaximumLength = (USHORT) UnicodeLength;
|
|
|
|
// Finally, convert the string to unicode
|
|
RtlAnsiStringToUnicodeString(DestinationString, &AnsiString, FALSE);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CTEAllocateString(
|
|
PNDIS_STRING String,
|
|
unsigned short MaximumLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates a data buffer for Length characters in an uninitialized
|
|
NDIS_STRING. The allocated space must be freed by a call to CTEFreeString.
|
|
|
|
|
|
Arguments:
|
|
|
|
String - A pointer to an NDIS_STRING variable with no
|
|
associated data buffer.
|
|
|
|
Length - The maximum length of the string. In Unicode, this is a
|
|
byte count.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the initialization succeeded. FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
String->Buffer = ExAllocatePool(
|
|
NonPagedPool,
|
|
MaximumLength + sizeof(UNICODE_NULL)
|
|
);
|
|
|
|
if (String->Buffer == NULL) {
|
|
return(FALSE);
|
|
}
|
|
|
|
String->Length = 0;
|
|
String->MaximumLength = MaximumLength + sizeof(UNICODE_NULL);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
LONG
|
|
CTELogEvent(
|
|
IN PVOID LoggerId,
|
|
IN ULONG EventCode,
|
|
IN ULONG UniqueEventValue,
|
|
IN USHORT NumStrings,
|
|
IN PVOID StringsList, OPTIONAL
|
|
IN ULONG DataSize,
|
|
IN PVOID Data OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function allocates an I/O error log record, fills it in and writes it
|
|
to the I/O error log.
|
|
|
|
|
|
Arguments:
|
|
|
|
LoggerId - Pointer to the driver object logging this event.
|
|
|
|
EventCode - Identifies the error message.
|
|
|
|
UniqueEventValue - Identifies this instance of a given error message.
|
|
|
|
NumStrings - Number of unicode strings in strings list.
|
|
|
|
DataSize - Number of bytes of data.
|
|
|
|
Strings - Array of pointers to unicode strings (PWCHAR).
|
|
|
|
Data - Binary dump data for this message, each piece being
|
|
aligned on word boundaries.
|
|
|
|
Return Value:
|
|
|
|
TDI_SUCCESS - The error was successfully logged.
|
|
TDI_BUFFER_TOO_SMALL - The error data was too large to be logged.
|
|
TDI_NO_RESOURCES - Unable to allocate memory.
|
|
|
|
Notes:
|
|
|
|
This code is paged and may not be called at raised IRQL.
|
|
|
|
--*/
|
|
{
|
|
PIO_ERROR_LOG_PACKET ErrorLogEntry;
|
|
ULONG PaddedDataSize;
|
|
ULONG PacketSize;
|
|
ULONG TotalStringsSize = 0;
|
|
USHORT i;
|
|
PWCHAR *Strings;
|
|
PWCHAR Tmp;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
Strings = (PWCHAR *) StringsList;
|
|
|
|
//
|
|
// Sum up the length of the strings
|
|
//
|
|
for (i=0; i<NumStrings; i++) {
|
|
PWCHAR currentString;
|
|
ULONG stringSize;
|
|
|
|
stringSize = sizeof(UNICODE_NULL);
|
|
currentString = Strings[i];
|
|
|
|
while (*currentString++ != UNICODE_NULL) {
|
|
stringSize += sizeof(WCHAR);
|
|
}
|
|
|
|
TotalStringsSize += stringSize;
|
|
}
|
|
|
|
if (DataSize % sizeof(ULONG)) {
|
|
PaddedDataSize = DataSize +
|
|
(sizeof(ULONG) - (DataSize % sizeof(ULONG)));
|
|
}
|
|
else {
|
|
PaddedDataSize = DataSize;
|
|
}
|
|
|
|
PacketSize = TotalStringsSize + PaddedDataSize;
|
|
|
|
if (PacketSize > CTE_MAX_EVENT_LOG_DATA_SIZE) {
|
|
return(TDI_BUFFER_TOO_SMALL); // Too much error data
|
|
}
|
|
|
|
//
|
|
// Now add in the size of the log packet, but subtract 4 from the data
|
|
// since the packet struct contains a ULONG for data.
|
|
//
|
|
if (PacketSize > sizeof(ULONG)) {
|
|
PacketSize += sizeof(IO_ERROR_LOG_PACKET) - sizeof(ULONG);
|
|
}
|
|
else {
|
|
PacketSize += sizeof(IO_ERROR_LOG_PACKET);
|
|
}
|
|
|
|
ASSERT(PacketSize <= ERROR_LOG_MAXIMUM_SIZE);
|
|
|
|
ErrorLogEntry = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry(
|
|
(PDRIVER_OBJECT) LoggerId,
|
|
(UCHAR) PacketSize
|
|
);
|
|
|
|
if (ErrorLogEntry == NULL) {
|
|
return(TDI_NO_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// Fill in the necessary log packet fields.
|
|
//
|
|
ErrorLogEntry->UniqueErrorValue = UniqueEventValue;
|
|
ErrorLogEntry->ErrorCode = EventCode;
|
|
ErrorLogEntry->NumberOfStrings = NumStrings;
|
|
ErrorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) +
|
|
(USHORT) PaddedDataSize - sizeof(ULONG);
|
|
ErrorLogEntry->DumpDataSize = (USHORT) PaddedDataSize;
|
|
|
|
//
|
|
// Copy the Dump Data to the packet
|
|
//
|
|
if (DataSize > 0) {
|
|
RtlMoveMemory(
|
|
(PVOID) ErrorLogEntry->DumpData,
|
|
Data,
|
|
DataSize
|
|
);
|
|
}
|
|
|
|
//
|
|
// Copy the strings to the packet.
|
|
//
|
|
Tmp = (PWCHAR) ((char *) ErrorLogEntry +
|
|
ErrorLogEntry->StringOffset +
|
|
PaddedDataSize);
|
|
|
|
for (i=0; i<NumStrings; i++) {
|
|
PWCHAR wchPtr = Strings[i];
|
|
|
|
while( (*Tmp++ = *wchPtr++) != UNICODE_NULL);
|
|
}
|
|
|
|
IoWriteErrorLogEntry(ErrorLogEntry);
|
|
|
|
return(TDI_SUCCESS);
|
|
}
|
|
|