Windows驱动开发入门-NDIS协议驱动源码

debug.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
debug.c
Abstract:
This module contains all debug-related code.
Revision History:
Notes:
--*/
#include "precomp.h"
#define __FILENUMBER 'GBED'
#if DBG
INT ndisprotDebugLevel = DL_WARN;
NDIS_SPIN_LOCK ndisprotDbgLogLock;
PNUIOD_ALLOCATION ndisprotdMemoryHead = (PNUIOD_ALLOCATION)NULL;
PNUIOD_ALLOCATION ndisprotdMemoryTail = (PNUIOD_ALLOCATION)NULL;
ULONG ndisprotdAllocCount = 0; // how many allocated so far (unfreed)
NDIS_SPIN_LOCK ndisprotdMemoryLock;
BOOLEAN ndisprotdInitDone = FALSE;
PVOID
ndisprotAuditAllocMem(
PVOID pPointer,
ULONG Size,
ULONG FileNumber,
ULONG LineNumber
) {
PVOID pBuffer;
PNUIOD_ALLOCATION pAllocInfo;
if (!ndisprotdInitDone) {
NdisAllocateSpinLock(&(ndisprotdMemoryLock));
ndisprotdInitDone = TRUE;
}
NdisAllocateMemoryWithTag(
(PVOID*)&pAllocInfo,
Size + sizeof(NUIOD_ALLOCATION),
(ULONG)'oiuN'
);
if (pAllocInfo == (PNUIOD_ALLOCATION)NULL) {
DEBUGP(DL_VERY_LOUD + 50,
("ndisprotAuditAllocMem: file %d, line %d, Size %d failed!\n",
FileNumber, LineNumber, Size));
pBuffer = NULL;
}
else {
pBuffer = (PVOID) & (pAllocInfo->UserData);
NPROT_SET_MEM(pBuffer, 0xaf, Size);
pAllocInfo->Signature = NUIOD_MEMORY_SIGNATURE;
pAllocInfo->FileNumber = FileNumber;
pAllocInfo->LineNumber = LineNumber;
pAllocInfo->Size = Size;
pAllocInfo->Location = (ULONG_PTR)pPointer;
pAllocInfo->Next = (PNUIOD_ALLOCATION)NULL;
NdisAcquireSpinLock(&(ndisprotdMemoryLock));
pAllocInfo->Prev = ndisprotdMemoryTail;
if (ndisprotdMemoryTail == (PNUIOD_ALLOCATION)NULL) {
// empty list
ndisprotdMemoryHead = ndisprotdMemoryTail = pAllocInfo;
}
else {
ndisprotdMemoryTail->Next = pAllocInfo;
}
ndisprotdMemoryTail = pAllocInfo;
ndisprotdAllocCount++;
NdisReleaseSpinLock(&(ndisprotdMemoryLock));
}
DEBUGP(DL_VERY_LOUD + 100,
("ndisprotAuditAllocMem: file %c%c%c%c, line %d, %d bytes, [0x%x] <- 0x%x\n",
(CHAR)(FileNumber & 0xff),
(CHAR)((FileNumber >> 8) & 0xff),
(CHAR)((FileNumber >> 16) & 0xff),
(CHAR)((FileNumber >> 24) & 0xff),
LineNumber, Size, pPointer, pBuffer));
return (pBuffer);
}
VOID
ndisprotAuditFreeMem(
PVOID Pointer
) {
PNUIOD_ALLOCATION pAllocInfo;
NdisAcquireSpinLock(&(ndisprotdMemoryLock));
pAllocInfo = CONTAINING_RECORD(Pointer, NUIOD_ALLOCATION, UserData);
if (pAllocInfo->Signature != NUIOD_MEMORY_SIGNATURE) {
DEBUGP(DL_ERROR,
("ndisprotAuditFreeMem: unknown buffer 0x%x!\n", Pointer));
NdisReleaseSpinLock(&(ndisprotdMemoryLock));
#if DBG
DbgBreakPoint();
#endif
return;
}
pAllocInfo->Signature = (ULONG)'DEAD';
if (pAllocInfo->Prev != (PNUIOD_ALLOCATION)NULL) {
pAllocInfo->Prev->Next = pAllocInfo->Next;
}
else {
ndisprotdMemoryHead = pAllocInfo->Next;
}
if (pAllocInfo->Next != (PNUIOD_ALLOCATION)NULL) {
pAllocInfo->Next->Prev = pAllocInfo->Prev;
}
else {
ndisprotdMemoryTail = pAllocInfo->Prev;
}
ndisprotdAllocCount--;
NdisReleaseSpinLock(&(ndisprotdMemoryLock));
NdisFreeMemory(pAllocInfo, 0, 0);
}
VOID
ndisprotAuditShutdown(
VOID
) {
if (ndisprotdInitDone) {
if (ndisprotdAllocCount != 0) {
DEBUGP(DL_ERROR, ("AuditShutdown: unfreed memory, %d blocks!\n",
ndisprotdAllocCount));
DEBUGP(DL_ERROR, ("MemoryHead: 0x%x, MemoryTail: 0x%x\n",
ndisprotdMemoryHead, ndisprotdMemoryTail));
DbgBreakPoint();
{
PNUIOD_ALLOCATION pAllocInfo;
while (ndisprotdMemoryHead != (PNUIOD_ALLOCATION)NULL) {
pAllocInfo = ndisprotdMemoryHead;
DEBUGP(DL_INFO, ("AuditShutdown: will free 0x%x\n", pAllocInfo));
ndisprotAuditFreeMem(&(pAllocInfo->UserData));
}
}
}
ndisprotdInitDone = FALSE;
}
}
#define MAX_HD_LENGTH 128
VOID
DbgPrintHexDump(
IN PUCHAR pBuffer,
IN ULONG Length
)
/*++
Routine Description:
Print a hex dump of the given contiguous buffer. If the length
is too long, we truncate it.
Arguments:
pBuffer - Points to start of data to be dumped
Length - Length of above.
Return Value:
None
--*/
{
ULONG i;
if (Length > MAX_HD_LENGTH) {
Length = MAX_HD_LENGTH;
}
for (i = 0; i < Length; i++) {
// Check if we are at the end of a line
if ((i > 0) && ((i & 0xf) == 0)) {
DbgPrint("\n");
}
// Print addr if we are at start of a new line
if ((i & 0xf) == 0) {
DbgPrint("%08x ", pBuffer);
}
DbgPrint(" %02x", *pBuffer++);
}
// Terminate the last line.
if (Length > 0) {
DbgPrint("\n");
}
}
#endif // DBG
#if DBG_SPIN_LOCK
ULONG ndisprotdSpinLockInitDone = 0;
NDIS_SPIN_LOCK ndisprotdLockLock;
VOID
ndisprotAllocateSpinLock(
IN PNPROT_LOCK pLock,
IN ULONG FileNumber,
IN ULONG LineNumber
) {
if (ndisprotdSpinLockInitDone == 0) {
ndisprotdSpinLockInitDone = 1;
NdisAllocateSpinLock(&(ndisprotdLockLock));
}
NdisAcquireSpinLock(&(ndisprotdLockLock));
pLock->Signature = NUIOL_SIG;
pLock->TouchedByFileNumber = FileNumber;
pLock->TouchedInLineNumber = LineNumber;
pLock->IsAcquired = 0;
pLock->OwnerThread = 0;
NdisAllocateSpinLock(&(pLock->NdisLock));
NdisReleaseSpinLock(&(ndisprotdLockLock));
}
VOID
ndisprotFreeSpinLock(
IN PNPROT_LOCK pLock,
IN ULONG FileNumber,
IN ULONG LineNumber
) {
NdisAcquireSpinLock(&(ndisprotdLockLock));
pLock->Signature = NUIOL_SIG;
pLock->TouchedByFileNumber = FileNumber;
pLock->TouchedInLineNumber = LineNumber;
pLock->IsAcquired = 0;
pLock->OwnerThread = 0;
NdisFreeSpinLock(&(pLock->NdisLock));
NdisReleaseSpinLock(&(ndisprotdLockLock));
}
VOID
ndisprotFreeDbgLock(
VOID
) {
ASSERT(ndisprotdSpinLockInitDone == 1);
ndisprotdSpinLockInitDone = 0;
NdisFreeSpinLock(&(ndisprotdLockLock));
}
VOID
ndisprotAcquireSpinLock(
IN PNPROT_LOCK pLock,
IN ULONG FileNumber,
IN ULONG LineNumber
) {
PKTHREAD pThread;
pThread = KeGetCurrentThread();
NdisAcquireSpinLock(&(ndisprotdLockLock));
if (pLock->Signature != NUIOL_SIG) {
DbgPrint("Trying to acquire uninited lock 0x%x, File %c%c%c%c, Line %d\n",
pLock,
(CHAR)(FileNumber & 0xff),
(CHAR)((FileNumber >> 8) & 0xff),
(CHAR)((FileNumber >> 16) & 0xff),
(CHAR)((FileNumber >> 24) & 0xff),
LineNumber);
DbgBreakPoint();
}
if (pLock->IsAcquired != 0) {
if (pLock->OwnerThread == pThread) {
DbgPrint("Detected multiple locking!: pLock 0x%x, File %c%c%c%c, Line %d\n",
pLock,
(CHAR)(FileNumber & 0xff),
(CHAR)((FileNumber >> 8) & 0xff),
(CHAR)((FileNumber >> 16) & 0xff),
(CHAR)((FileNumber >> 24) & 0xff),
LineNumber);
DbgPrint("pLock 0x%x already acquired in File %c%c%c%c, Line %d\n",
pLock,
(CHAR)(pLock->TouchedByFileNumber & 0xff),
(CHAR)((pLock->TouchedByFileNumber >> 8) & 0xff),
(CHAR)((pLock->TouchedByFileNumber >> 16) & 0xff),
(CHAR)((pLock->TouchedByFileNumber >> 24) & 0xff),
pLock->TouchedInLineNumber);
DbgBreakPoint();
}
}
pLock->IsAcquired++;
NdisReleaseSpinLock(&(ndisprotdLockLock));
NdisAcquireSpinLock(&(pLock->NdisLock));
// Mark this lock.
pLock->OwnerThread = pThread;
pLock->TouchedByFileNumber = FileNumber;
pLock->TouchedInLineNumber = LineNumber;
}
VOID
ndisprotReleaseSpinLock(
IN PNPROT_LOCK pLock,
IN ULONG FileNumber,
IN ULONG LineNumber
) {
NdisDprAcquireSpinLock(&(ndisprotdLockLock));
if (pLock->Signature != NUIOL_SIG) {
DbgPrint("Trying to release uninited lock 0x%x, File %c%c%c%c, Line %d\n",
pLock,
(CHAR)(FileNumber & 0xff),
(CHAR)((FileNumber >> 8) & 0xff),
(CHAR)((FileNumber >> 16) & 0xff),
(CHAR)((FileNumber >> 24) & 0xff),
LineNumber);
DbgBreakPoint();
}

if (pLock->IsAcquired == 0) {
DbgPrint("Detected release of unacquired lock 0x%x, File %c%c%c%c, Line %d\n",
pLock,
(CHAR)(FileNumber & 0xff),
(CHAR)((FileNumber >> 8) & 0xff),
(CHAR)((FileNumber >> 16) & 0xff),
(CHAR)((FileNumber >> 24) & 0xff),
LineNumber);
DbgBreakPoint();
}
pLock->TouchedByFileNumber = FileNumber;
pLock->TouchedInLineNumber = LineNumber;
pLock->IsAcquired--;
pLock->OwnerThread = 0;
NdisDprReleaseSpinLock(&(ndisprotdLockLock));
NdisReleaseSpinLock(&(pLock->NdisLock));
}
#endif // DBG_SPIN_LOCK

debug.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
debug.h
Abstract:
Debug macros for NDISPROT
Notes:
--*/
#ifndef _NUIODEBUG__H
#define _NUIODEBUG__H
// Message verbosity: lower values indicate higher urgency
#define DL_EXTRA_LOUD 20
#define DL_VERY_LOUD 10
#define DL_LOUD 8
#define DL_INFO 6
#define DL_WARN 4
#define DL_ERROR 2
#define DL_FATAL 0
#if DBG_SPIN_LOCK
typedef struct _NPROT_LOCK {
ULONG Signature;
ULONG IsAcquired;
PKTHREAD OwnerThread;
ULONG TouchedByFileNumber;
ULONG TouchedInLineNumber;
NDIS_SPIN_LOCK NdisLock;
} NPROT_LOCK, * PNPROT_LOCK;
#define NUIOL_SIG 'KCOL'
extern NDIS_SPIN_LOCK ndisprotDbgLogLock;
extern
VOID
ndisprotAllocateSpinLock(
IN PNPROT_LOCK pLock,
IN ULONG FileNumber,
IN ULONG LineNumber
);
extern
VOID
ndisprotFreeSpinLock(
IN PNPROT_LOCK pLock,
IN ULONG FileNumber,
IN ULONG LineNumber
);
extern
VOID
ndisprotAcquireSpinLock(
IN PNPROT_LOCK pLock,
IN ULONG FileNumber,
IN ULONG LineNumber
);
extern
VOID
ndisprotReleaseSpinLock(
IN PNPROT_LOCK pLock,
IN ULONG FileNumber,
IN ULONG LineNumber
);
extern
VOID
ndisprotFreeDbgLock(
VOID
);
#define CHECK_LOCK_COUNT(Count) \
{ \
if ((INT)(Count) < 0) \
{ \
DbgPrint("Lock Count %d is < 0! File %s, Line %d\n",\
Count, __FILE__, __LINE__); \
DbgBreakPoint(); \
} \
}
#else
#define CHECK_LOCK_COUNT(Count)
typedef NDIS_SPIN_LOCK NPROT_LOCK;
typedef PNDIS_SPIN_LOCK PNPROT_LOCK;
#endif // DBG_SPIN_LOCK
#if DBG
extern INT ndisprotDebugLevel;
#define DEBUGP(lev, stmt) \
{ \
if ((lev) <= ndisprotDebugLevel) \
{ \
DbgPrint("NdisProt: "); DbgPrint stmt; \
} \
}
#define DEBUGPDUMP(lev, pBuf, Len) \
{ \
if ((lev) <= ndisprotDebugLevel) \
{ \
DbgPrintHexDump((PUCHAR)(pBuf), (ULONG)(Len)); \
} \
}
#define NPROT_ASSERT(exp) \
{ \
if (!(exp)) \
{ \
DbgPrint("NdisProt: assert " #exp " failed in" \
" file %s, line %d\n", __FILE__, __LINE__); \
DbgBreakPoint(); \
} \
}
#define NPROT_SET_SIGNATURE(s, t)\
(s)->t##_sig = t##_signature;
#define NPROT_STRUCT_ASSERT(s, t) \
if ((s)->t##_sig != t##_signature) \
{ \
DbgPrint("ndisprot: assertion failure" \
" for type " #t " at 0x%x in file %s, line %d\n", \
(PUCHAR)s, __FILE__, __LINE__); \
DbgBreakPoint(); \
}
// Memory Allocation/Freeing Audit:
// The NUIOD_ALLOCATION structure stores all info about one allocation
typedef struct _NUIOD_ALLOCATION {
ULONG Signature;
struct _NUIOD_ALLOCATION* Next;
struct _NUIOD_ALLOCATION* Prev;
ULONG FileNumber;
ULONG LineNumber;
ULONG Size;
ULONG_PTR Location; // where the returned ptr was stored
union {
ULONGLONG Alignment;
UCHAR UserData;
};
} NUIOD_ALLOCATION, * PNUIOD_ALLOCATION;
#define NUIOD_MEMORY_SIGNATURE (ULONG)'CSII'
extern
PVOID
ndisprotAuditAllocMem(
PVOID pPointer,
ULONG Size,
ULONG FileNumber,
ULONG LineNumber
);
extern
VOID
ndisprotAuditFreeMem(
PVOID Pointer
);
extern
VOID
ndisprotAuditShutdown(
VOID
);
extern
VOID
DbgPrintHexDump(
PUCHAR pBuffer,
ULONG Length
);
// No debug
#define DEBUGP(lev, stmt)
#define DEBUGPDUMP(lev, pBuf, Len)
#define NPROT_ASSERT(exp)
#define NPROT_SET_SIGNATURE(s, t)
#define NPROT_STRUCT_ASSERT(s, t)
#endif // DBG
#endif // _NUIODEBUG__H

excallbk.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/*++
Copyright (c) Microsoft Corporation. All rights reserved.
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE.
Module Name:
ExCallbk.c
Abstract: The routines in this module helps to solve driver load order
dependency between this sample and NDISWDM sample. These
routines are not required in a typical protocol driver. By default
this module is not included in the sample. You include these routines
by adding EX_CALLBACK defines to the 'sources' file. Read the
NDISWDM samples readme file for more information on how ExCallback
kernel interfaces are used to solve driver load order issue.
Environment:
Kernel mode
Revision History:
--*/
#include "precomp.h"
#ifdef EX_CALLBACK
#define __FILENUMBER 'LCxE'
#define NDISPROT_CALLBACK_NAME L"\\Callback\\NdisProtCallbackObject"
#define CALLBACK_SOURCE_NDISPROT 0
#define CALLBACK_SOURCE_NDISWDM 1
PCALLBACK_OBJECT CallbackObject = NULL;
PVOID CallbackRegisterationHandle = NULL;
typedef VOID(*NOTIFY_PRESENCE_CALLBACK)(OUT PVOID Source);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, ndisprotRegisterExCallBack)
#pragma alloc_text(PAGE, ndisprotUnregisterExCallBack)
#endif // ALLOC_PRAGMA
BOOLEAN
ndisprotRegisterExCallBack(VOID)
/*++
Routine Description:
Create or open an existing callback object and send a
notification. Note that the system calls our notication
callback routine in addition to notifying other
registered clients.
Arguments:
Return Value:
TRUE or FALSE
--*/
{
OBJECT_ATTRIBUTES ObjectAttr;
UNICODE_STRING CallBackObjectName;
NTSTATUS Status;
BOOLEAN bResult = TRUE;
DEBUGP(DL_LOUD, ("--> ndisprotRegisterExCallBack\n"));
PAGED_CODE();
do {
RtlInitUnicodeString(&CallBackObjectName, NDISPROT_CALLBACK_NAME);
InitializeObjectAttributes(&ObjectAttr,
&CallBackObjectName,
OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
NULL,
NULL);
Status = ExCreateCallback(&CallbackObject,
&ObjectAttr,
TRUE,
TRUE);
if (!NT_SUCCESS(Status)) {
DEBUGP(DL_ERROR, ("RegisterExCallBack: failed to create callback %lx\n", Status));
bResult = FALSE;
break;
}
CallbackRegisterationHandle = ExRegisterCallback(CallbackObject,
ndisprotCallback,
(PVOID)NULL);
if (CallbackRegisterationHandle == NULL) {
DEBUGP(DL_ERROR, ("RegisterExCallBack: failed to register a Callback routine%lx\n", Status));
bResult = FALSE;
break;
}
ExNotifyCallback(CallbackObject,
(PVOID)CALLBACK_SOURCE_NDISPROT,
(PVOID)NULL);
} while (FALSE);
if (!bResult) {
if (CallbackRegisterationHandle) {
ExUnregisterCallback(CallbackRegisterationHandle);
CallbackRegisterationHandle = NULL;
}
if (CallbackObject) {
ObDereferenceObject(CallbackObject);
CallbackObject = NULL;
}
}
DEBUGP(DL_LOUD, ("<-- ndisprotRegisterExCallBack\n"));
return bResult;
}
VOID
ndisprotUnregisterExCallBack()
/*++
Routine Description:
Unregister out callback routine and also delete the object
by removing the reference on it.
Arguments:
Return Value:
VOID
--*/
{
DEBUGP(DL_LOUD, ("--> ndisprotUnregisterExCallBack\n"));
PAGED_CODE();
if (CallbackRegisterationHandle) {
ExUnregisterCallback(CallbackRegisterationHandle);
CallbackRegisterationHandle = NULL;
}
if (CallbackObject) {
ObDereferenceObject(CallbackObject);
CallbackObject = NULL;
}
DEBUGP(DL_LOUD, ("<-- ndisprotUnregisterExCallBack\n"));
}
VOID
ndisprotCallback(
PVOID CallBackContext,
PVOID Source,
PVOID CallbackAddr
)
/*++
Routine Description:
This is the notification callback routine called by the executive
subsystem in the context of the thread that make the notification
on the object we have registered. This routine could be called
by the NDISPROT driver or another instance of our own miniport.
Arguments:
CallBackContext - This is the context value specified in the
ExRegisterCallback call. It's NULL.
Source - First parameter specified in the call to ExNotifyCallback.
This is used to indenty the caller. This could be
either ourself or NDISWDM.
CallbackAddr - Second parameter specified in the call to ExNotifyCallback.
This is an additonal context the caller can specify. When it
comes from NDISWDM, it's a routine to call as part of
the notification.
Return Value:
VOID
--*/
{
NOTIFY_PRESENCE_CALLBACK func;
DEBUGP(DL_LOUD, ("==>ndisprotoCallback: Source %lx, CallbackAddr %p\n",
Source, CallbackAddr));
// if we are the one issuing this notification, just return
if (Source == CALLBACK_SOURCE_NDISPROT) {
return;
}
// Notification is coming from NDISWDM
// let it know that you are here
ASSERT(Source == (PVOID)CALLBACK_SOURCE_NDISWDM);
if (Source == (PVOID)CALLBACK_SOURCE_NDISWDM) {
ASSERT(CallbackAddr);
if (CallbackAddr == NULL) {
DEBUGP(DL_ERROR, ("Callback called with invalid address %p\n", CallbackAddr));
return;
}
func = CallbackAddr;
func(CALLBACK_SOURCE_NDISPROT);
}
DEBUGP(DL_LOUD, ("<==ndisprotoCallback: Source, %lx\n", Source));
}
#endif

macros.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
macros.h
Abstract:
Some macros for NDISPROT.
Environment:
Kernel mode only.
Revision History:
--*/
#ifndef MIN
#define MIN(_a, _b) ((_a) < (_b)? (_a): (_b))
#endif
#if DBG
#define NPROT_REF_OPEN(_pOpen) ndisprotDbgRefOpen(_pOpen, __FILENUMBER, __LINE__)
#define NPROT_DEREF_OPEN(_pOpen) ndisprotDbgDerefOpen(_pOpen, __FILENUMBER, __LINE__)
#else
#define NPROT_REF_OPEN(_pOpen) ndisprotRefOpen(_pOpen)
#define NPROT_DEREF_OPEN(_pOpen) ndisprotDerefOpen(_pOpen)
#endif
// Spinlock macros
#if DBG_SPIN_LOCK
#define NPROT_INIT_LOCK(_pLock) \
ndisprotAllocateSpinLock(_pLock, __FILENUMBER, __LINE__)
#define NPROT_FREE_LOCK(_pLock) \
ndisprotFreeSpinLock(_pLock, __FILENUMBER, __LINE__)
#define NPROT_ACQUIRE_LOCK(_pLock) \
ndisprotAcquireSpinLock(_pLock, __FILENUMBER, __LINE__)
#define NPROT_RELEASE_LOCK(_pLock) \
ndisprotReleaseSpinLock(_pLock, __FILENUMBER, __LINE__)
#define NPROT_FREE_DBG_LOCK() \
ndisprotFreeDbgLock()
#else
#define NPROT_INIT_LOCK(_pLock) NdisAllocateSpinLock(_pLock)
#define NPROT_FREE_LOCK(_pLock) NdisFreeSpinLock(_pLock)
#define NPROT_ACQUIRE_LOCK(_pLock) NdisAcquireSpinLock(_pLock)
#define NPROT_RELEASE_LOCK(_pLock) NdisReleaseSpinLock(_pLock)
#define NPROT_FREE_DBG_LOCK()
#endif // DBG
// List manipulation.
#define NPROT_INIT_LIST_HEAD(_pList) InitializeListHead(_pList)
#define NPROT_IS_LIST_EMPTY(_pList) IsListEmpty(_pList)
#define NPROT_INSERT_HEAD_LIST(_pList, _pEnt) InsertHeadList(_pList, _pEnt)
#define NPROT_INSERT_TAIL_LIST(_pList, _pEnt) InsertTailList(_pList, _pEnt)
#define NPROT_REMOVE_ENTRY_LIST(_pEnt) RemoveEntryList(_pEnt)
// Receive packet queueing.
#define NPROT_LIST_ENTRY_TO_RCV_PKT(_pEnt) \
CONTAINING_RECORD(CONTAINING_RECORD(_pEnt, NPROT_RECV_PACKET_RSVD, Link), NDIS_PACKET, ProtocolReserved)
#define NPROT_RCV_PKT_TO_LIST_ENTRY(_pPkt) \
(&((PNPROT_RECV_PACKET_RSVD)&((_pPkt)->ProtocolReserved[0]))->Link)
// In case we allocate a receive packet of our own to copy and queue
// received data, we might have to also allocate an auxiliary NDIS_BUFFER
// to map part of the receive buffer (skipping the header bytes), so as
// to satisfy NdisTransferData. In such cases, we keep a pointer to the
// fully mapped receive buffer in the packet reserved space:
#define NPROT_RCV_PKT_TO_ORIGINAL_BUFFER(_pPkt) \
(((PNPROT_RECV_PACKET_RSVD)&((_pPkt)->ProtocolReserved[0]))->pOriginalBuffer)
// Send packet context.
#define NPROT_IRP_FROM_SEND_PKT(_pPkt) \
(((PNPROT_SEND_PACKET_RSVD)&((_pPkt)->ProtocolReserved[0]))->pIrp)
#define NPROT_SEND_PKT_RSVD(_pPkt) \
((PNPROT_SEND_PACKET_RSVD)&((_pPkt)->ProtocolReserved[0]))
#define NPROT_REF_SEND_PKT(_pPkt) \
(VOID)NdisInterlockedIncrement((PLONG)&NPROT_SEND_PKT_RSVD(_pPkt)->RefCount)
#define NPROT_DEREF_SEND_PKT(_pPkt) \
{ \
if (NdisInterlockedDecrement((PLONG)&NPROT_SEND_PKT_RSVD(_pPkt)->RefCount) == 0) \
{ \
NdisFreePacket(_pPkt); \
} \
}
#ifdef NDIS51
// Cancel IDs are generated by using the partial cancel ID we got from
// NDIS ORed with a monotonically increasing locally generated ID.
#define NPROT_CANCEL_ID_LOW_MASK (((ULONG_PTR)-1) >> 8)
#define NPROT_GET_NEXT_CANCEL_ID() \
(PVOID)(Globals.PartialCancelId | \
((NdisInterlockedIncrement((PLONG)&Globals.LocalCancelId)) & NPROT_CANCEL_ID_LOW_MASK))
#endif // NDIS51
// Memory allocation
#if DBG
#define NPROT_ALLOC_MEM(_pVar, _Size) \
(_pVar) = ndisprotAuditAllocMem( \
(PVOID)&(_pVar), \
_Size, \
__FILENUMBER, \
__LINE__);
#define NPROT_FREE_MEM(_pMem) \
ndisprotAuditFreeMem(_pMem);
#else
#define NPROT_ALLOC_MEM(_pVar, _Size) \
NdisAllocateMemoryWithTag((PVOID *)(&_pVar), (_Size), NPROT_ALLOC_TAG)
#define NPROT_FREE_MEM(_pMem) \
NdisFreeMemory(_pMem, 0, 0)
#endif // DBG
#define NPROT_ZERO_MEM(_pMem, _ByteCount) \
NdisZeroMemory(_pMem, _ByteCount)
#define NPROT_COPY_MEM(_pDst, _pSrc, _ByteCount) \
NdisMoveMemory(_pDst, _pSrc, _ByteCount)
#define NPROT_MEM_CMP(_p1, _p2, _ByteCount) \
NdisEqualMemory(_p1, _p2, _ByteCount)
#define NPROT_SET_MEM(_pMem, _ByteVal, _ByteCount) \
NdisFillMemory(_pMem, _ByteCount, _ByteVal)
// Events.
#define NPROT_INIT_EVENT(_pEvent) NdisInitializeEvent(_pEvent)
#define NPROT_SIGNAL_EVENT(_pEvent) NdisSetEvent(_pEvent)
#define NPROT_WAIT_EVENT(_pEvent, _MsToWait) NdisWaitEvent(_pEvent, _MsToWait)
// Flags
#define NPROT_SET_FLAGS(_FlagsVar, _Mask, _BitsToSet) \
(_FlagsVar) = ((_FlagsVar) & ~(_Mask)) | (_BitsToSet)
#define NPROT_TEST_FLAGS(_FlagsVar, _Mask, _BitsToCheck) \
(((_FlagsVar) & (_Mask)) == (_BitsToCheck))
// Block the calling thread for the given duration:
#define NPROT_SLEEP(_Seconds) \
{ \
NDIS_EVENT _SleepEvent; \
NdisInitializeEvent(&_SleepEvent); \
(VOID)NdisWaitEvent(&_SleepEvent, _Seconds*1000); \
}
#define NDIS_STATUS_TO_NT_STATUS(_NdisStatus, _pNtStatus) \
{ \
/* \
* The following NDIS status codes map directly to NT status codes. \
*/ \
if (((NDIS_STATUS_SUCCESS == (_NdisStatus)) || \
(NDIS_STATUS_PENDING == (_NdisStatus)) || \
(NDIS_STATUS_BUFFER_OVERFLOW == (_NdisStatus)) || \
(NDIS_STATUS_FAILURE == (_NdisStatus)) || \
(NDIS_STATUS_RESOURCES == (_NdisStatus)) || \
(NDIS_STATUS_NOT_SUPPORTED == (_NdisStatus)))) \
{ \
*(_pNtStatus) = (NTSTATUS)(_NdisStatus); \
} \
else if (NDIS_STATUS_BUFFER_TOO_SHORT == (_NdisStatus)) \
{ \
/* \
* The above NDIS status codes require a little special casing. \
*/ \
*(_pNtStatus) = STATUS_BUFFER_TOO_SMALL; \
} \
else if (NDIS_STATUS_INVALID_LENGTH == (_NdisStatus)) \
{ \
*(_pNtStatus) = STATUS_INVALID_BUFFER_SIZE; \
} \
else if (NDIS_STATUS_INVALID_DATA == (_NdisStatus)) \
{ \
*(_pNtStatus) = STATUS_INVALID_PARAMETER; \
} \
else if (NDIS_STATUS_ADAPTER_NOT_FOUND == (_NdisStatus)) \
{ \
*(_pNtStatus) = STATUS_NO_MORE_ENTRIES; \
} \
else if (NDIS_STATUS_ADAPTER_NOT_READY == (_NdisStatus)) \
{ \
*(_pNtStatus) = STATUS_DEVICE_NOT_READY; \
} \
else \
{ \
*(_pNtStatus) = STATUS_UNSUCCESSFUL; \
} \
}

ndisbind.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
ndisbind.c
Abstract:
NDIS protocol entry points and utility routines to handle binding
and unbinding from adapters.
Environment:
Kernel mode only.
Revision History:
--*/
#include "precomp.h"
#define __FILENUMBER 'DNIB'
VOID NdisProtBindAdapter(OUT PNDIS_STATUS pStatus, IN NDIS_HANDLE BindContext, IN PNDIS_STRING pDeviceName, IN PVOID SystemSpecific1, IN PVOID SystemSpecific2)
/*++
Routine Description:
Protocol Bind Handler entry point called when NDIS wants us to bind to an adapter. We go ahead and set up a binding. An OPEN_CONTEXT structure is allocated to keep state about this binding.
Arguments:
pStatus - place to return bind status
BindContext - handle to use with NdisCompleteBindAdapter
DeviceName - adapter to bind to
SystemSpecific1 - used to access protocol-specific registry key for this binding
SystemSpecific2 - unused
Return Value:
None
--*/
{
PNDISPROT_OPEN_CONTEXT pOpenContext;
NDIS_STATUS Status, ConfigStatus;
NDIS_HANDLE ConfigHandle;
UNREFERENCED_PARAMETER(BindContext);
UNREFERENCED_PARAMETER(SystemSpecific2);
do {
// Allocate our context for this open.
// 分配空间给每个打开上下文。所谓打开上下文就是每次绑定,用户分配的一片空间,用来保存这次绑定相关的信息。这里用宏NPROT_ALLOC_MEM分配内存是为了调试的方便。实际上本质是用NdisAllocateMemoryWithTag分配空间。读者如果用ExAllocatePoolWithTag代替也是可行的。只是要注意必须是Nonpaged空间。
NPROT_ALLOC_MEM(pOpenContext, sizeof(NDISPROT_OPEN_CONTEXT));
if (pOpenContext == NULL) {
Status = NDIS_STATUS_RESOURCES;
break;
};
// 内存清0。同样用宏。实际上用的NdisZeroMemory。
NPROT_ZERO_MEM(pOpenContext, sizeof(NDISPROT_OPEN_CONTEXT));
// 给这个空间写一个特征数据便于识别判错。
NPROT_SET_SIGNATURE(pOpenContext, oc);
// 初始化几个用到的数据成员。锁、读队列、写对队列、包队列
// 电源打开事件
NPROT_INIT_LOCK(&pOpenContext->Lock);
NPROT_INIT_LIST_HEAD(&pOpenContext->PendedReads);
NPROT_INIT_LIST_HEAD(&pOpenContext->PendedWrites);
NPROT_INIT_LIST_HEAD(&pOpenContext->RecvPktQueue);
NPROT_INIT_EVENT(&pOpenContext->PoweredUpEvent);
// Start off by assuming that the device below is powered up.
// 认为开始的时候电源是打开的。
NPROT_SIGNAL_EVENT(&pOpenContext->PoweredUpEvent);
// Determine the platform we are running on.
// 下面开始检测我们运行在什么平台。首先假定是Win9x.但是为了去掉多余的部分,实际上我已经去掉了对Win9x的支持。所以下面这一段已经没有意义了。但是下面的代码依然有参考价值。实际上是在读取注册表的配置。
//pOpenContext->bRunningOnWin9x = TRUE;
//NdisOpenProtocolConfiguration(&ConfigStatus,&ConfigHandle,(PNDIS_STRING)SystemSpecific1);
//if (ConfigStatus == NDIS_STATUS_SUCCESS){
// PNDIS_CONFIGURATION_PARAMETER pParameter;
// NDIS_STRING VersionKey = NDIS_STRING_CONST("Environment");
// NdisReadConfiguration(&ConfigStatus,&pParameter,ConfigHandle,&VersionKey,NdisParameterInteger);
// if ((ConfigStatus == NDIS_STATUS_SUCCESS) && ((pParameter->ParameterType == NdisParameterInteger) || (pParameter->ParameterType == NdisParameterHexInteger)))
// pOpenContext->bRunningOnWin9x = (pParameter->ParameterData.IntegerData == NdisEnvironmentWindows);
// NdisCloseConfiguration(ConfigHandle);
//};
NPROT_REF_OPEN(pOpenContext);
// Add it to the global list.
// 因为打开上下文已经被分配好。所以这里将这个打开上下文保存到全局链表里以便日后检索。注意这个操作要加锁。实际上这里用的就是读者前面学过的自旋锁。
NPROT_ACQUIRE_LOCK(&Globals.GlobalLock);
NPROT_INSERT_TAIL_LIST(&Globals.OpenList, &pOpenContext->Link);
NPROT_RELEASE_LOCK(&Globals.GlobalLock);
// 正式的绑定过程。
Status = ndisprotCreateBinding(pOpenContext, (PUCHAR)pDeviceName->Buffer, pDeviceName->Length);
if (Status != NDIS_STATUS_SUCCESS)
break;
} while (FALSE);
*pStatus = Status;
return;
};
VOID
NdisProtOpenAdapterComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status,
IN NDIS_STATUS OpenErrorCode
)
/*++
Routine Description:
Completion routine called by NDIS if our call to NdisOpenAdapter
pends. Wake up the thread that called NdisOpenAdapter.
Arguments:
ProtocolBindingContext - pointer to open context structure
Status - status of the open
OpenErrorCode - if unsuccessful, additional information
Return Value:
None
--*/
{
PNDISPROT_OPEN_CONTEXT pOpenContext;
UNREFERENCED_PARAMETER(OpenErrorCode);
pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext;
NPROT_STRUCT_ASSERT(pOpenContext, oc);
pOpenContext->BindStatus = Status;
NPROT_SIGNAL_EVENT(&pOpenContext->BindEvent);
}
VOID NdisProtUnbindAdapter(OUT PNDIS_STATUS pStatus, IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_HANDLE UnbindContext)
/*++
Routine Description:
NDIS calls this when it wants us to close the binding to an adapter.
Arguments:
pStatus - place to return status of Unbind
ProtocolBindingContext - pointer to open context structure
UnbindContext - to use in NdisCompleteUnbindAdapter if we return pending
Return Value:
None
--*/
{
PNDISPROT_OPEN_CONTEXT pOpenContext;
UNREFERENCED_PARAMETER(UnbindContext);
pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext;
NPROT_STRUCT_ASSERT(pOpenContext, oc);
// Mark this open as having seen an Unbind.
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
NPROT_SET_FLAGS(pOpenContext->Flags, NUIOO_UNBIND_FLAGS, NUIOO_UNBIND_RECEIVED); //无条件设置接收到解绑的标记
// In case we had threads blocked for the device below to be powered up, wake them up.
NPROT_SIGNAL_EVENT(&pOpenContext->PoweredUpEvent); //设置事件 通知所有在等待电源启动的线程 避免一些请求无法完成
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
ndisprotShutdownBinding(pOpenContext); //解绑
*pStatus = NDIS_STATUS_SUCCESS;
return;
};
VOID
NdisProtCloseAdapterComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status
)
/*++
Routine Description:
Called by NDIS to complete a pended call to NdisCloseAdapter.
We wake up the thread waiting for this completion.
Arguments:
ProtocolBindingContext - pointer to open context structure
Status - Completion status of NdisCloseAdapter
Return Value:
None
--*/
{
PNDISPROT_OPEN_CONTEXT pOpenContext;
pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext;
NPROT_STRUCT_ASSERT(pOpenContext, oc);
pOpenContext->BindStatus = Status;
NPROT_SIGNAL_EVENT(&pOpenContext->BindEvent);
}
NDIS_STATUS
NdisProtPnPEventHandler(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNET_PNP_EVENT pNetPnPEvent
)
/*++
Routine Description:
Called by NDIS to notify us of a PNP event. The most significant
one for us is power state change.
Arguments:
ProtocolBindingContext - pointer to open context structure
this is NULL for global reconfig events.
pNetPnPEvent - pointer to the PNP event
Return Value:
Our processing status for the PNP event.
--*/
{
PNDISPROT_OPEN_CONTEXT pOpenContext;
NDIS_STATUS Status;
pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext;
switch (pNetPnPEvent->NetEvent) {
case NetEventSetPower:
NPROT_STRUCT_ASSERT(pOpenContext, oc);
pOpenContext->PowerState = *(PNET_DEVICE_POWER_STATE)pNetPnPEvent->Buffer;
if (pOpenContext->PowerState > NetDeviceStateD0) {
// The device below is transitioning to a low power state.
// Block any threads attempting to query the device while
// in this state.
NPROT_INIT_EVENT(&pOpenContext->PoweredUpEvent);
// Wait for any I/O in progress to complete.
ndisprotWaitForPendingIO(pOpenContext, FALSE);
// Return any receives that we had queued up.
ndisprotFlushReceiveQueue(pOpenContext);
DEBUGP(DL_INFO, ("PnPEvent: Open %p, SetPower to %d\n",
pOpenContext, pOpenContext->PowerState));
}
else {
// The device below is powered up.
DEBUGP(DL_INFO, ("PnPEvent: Open %p, SetPower ON: %d\n",
pOpenContext, pOpenContext->PowerState));
NPROT_SIGNAL_EVENT(&pOpenContext->PoweredUpEvent);
}
Status = NDIS_STATUS_SUCCESS;
break;
case NetEventQueryPower:
Status = NDIS_STATUS_SUCCESS;
break;
case NetEventBindsComplete:
NPROT_SIGNAL_EVENT(&Globals.BindsComplete);
if (!ndisprotRegisterExCallBack()) {
DEBUGP(DL_ERROR, ("DriverEntry: ndisprotRegisterExCallBack failed\n"));
}
Status = NDIS_STATUS_SUCCESS;
break;
case NetEventQueryRemoveDevice:
case NetEventCancelRemoveDevice:
case NetEventReconfigure:
case NetEventBindList:
case NetEventPnPCapabilities:
Status = NDIS_STATUS_SUCCESS;
break;
default:
Status = NDIS_STATUS_NOT_SUPPORTED;
break;
}
DEBUGP(DL_INFO, ("PnPEvent: Open %p, Event %d, Status %x\n",
pOpenContext, pNetPnPEvent->NetEvent, Status));
return (Status);
}
VOID
NdisProtProtocolUnloadHandler(
VOID
)
/*++
Routine Description:
NDIS calls this on a usermode request to uninstall us.
Arguments:
None
Return Value:
None
--*/
{
ndisprotDoProtocolUnload();
}
NDIS_STATUS ndisprotCreateBinding(IN PNDISPROT_OPEN_CONTEXT pOpenContext,__in_bcount(BindingInfoLength) IN PUCHAR pBindingInfo,IN ULONG BindingInfoLength) {
NDIS_STATUS Status;
NDIS_STATUS OpenErrorCode;
NDIS_MEDIUM MediumArray[1] = { NdisMedium802_3 };
UINT SelectedMediumIndex;
PNDISPROT_OPEN_CONTEXT pTmpOpenContext;
BOOLEAN fDoNotDisturb = FALSE;
BOOLEAN fOpenComplete = FALSE;
ULONG BytesProcessed;
ULONG GenericUlong = 0;
// 输出一句调试信息。
DEBUGP(DL_LOUD, ("CreateBinding: open %p/%x, device [%ws]\n",pOpenContext, pOpenContext->Flags, pBindingInfo));
Status = NDIS_STATUS_SUCCESS;
do {
// 检查看看是否已经绑定了这个网卡。如果已经绑定的话,就没有必要再次绑定了,直接返回成功即可。请注意,ndisprotLookupDevice会给这个打开上下文增加一个引用。
pTmpOpenContext = ndisprotLookupDevice(pBindingInfo, BindingInfoLength);
// 如果没有找到的话,就返回NULL了。
if (pTmpOpenContext != NULL) {
DEBUGP(DL_WARN, ("CreateBinding: Binding to device %ws already exists on open %p\n", pTmpOpenContext->DeviceName.Buffer, pTmpOpenContext));
// 减少这个打开上下文的一个引用。
NPROT_DEREF_OPEN(pTmpOpenContext);
Status = NDIS_STATUS_FAILURE;
break;
};
// 获得锁。为什么这里要用锁?这是因为只有对空闲的打开上下文
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
// 通过标记来检查...如果绑定标志不是空闲状态,或者解除绑定信息收到了解除绑定的要求,那就直接返回失败。
if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_IDLE) || NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_UNBIND_FLAGS, NUIOO_UNBIND_RECEIVED)) {
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
Status = NDIS_STATUS_NOT_ACCEPTED;
// 设置了标记,表示
fDoNotDisturb = TRUE;
break;
};
// 设置标记,表示我们已经开始绑定了。别人勿操作它。
NPROT_SET_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_OPENING);
// 释放锁。
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
// 分配名字。到这里开始绑定了。先分配设备名字符串。
NPROT_ALLOC_MEM(pOpenContext->DeviceName.Buffer, BindingInfoLength + sizeof(WCHAR));
if (pOpenContext->DeviceName.Buffer == NULL) {
DEBUGP(DL_WARN, ("CreateBinding: failed to alloc device name buf (%d bytes)\n", BindingInfoLength + sizeof(WCHAR)));
Status = NDIS_STATUS_RESOURCES;
break;
};
// 从pBindingInfo中把字符串拷贝出来。
NPROT_COPY_MEM(pOpenContext->DeviceName.Buffer, pBindingInfo, BindingInfoLength);
#pragma prefast(suppress: 12009, "DeviceName length will not cause overflow")
* (PWCHAR)((PUCHAR)pOpenContext->DeviceName.Buffer + BindingInfoLength) = L'\0';
NdisInitUnicodeString(&pOpenContext->DeviceName, pOpenContext->DeviceName.Buffer);
// 分配包池。用来做发送缓冲区,容纳将要发送出去的包。
NdisAllocatePacketPoolEx(&Status,&pOpenContext->SendPacketPool,MIN_SEND_PACKET_POOL_SIZE,MAX_SEND_PACKET_POOL_SIZE - MIN_SEND_PACKET_POOL_SIZE,sizeof(NPROT_SEND_PACKET_RSVD));
if (Status != NDIS_STATUS_SUCCESS) {
DEBUGP(DL_WARN, ("CreateBinding: failed to alloc send packet pool: %x\n", Status));
break;
};
// 分配包池,用来容纳
NdisAllocatePacketPoolEx(&Status,&pOpenContext->RecvPacketPool,MIN_RECV_PACKET_POOL_SIZE,MAX_RECV_PACKET_POOL_SIZE - MIN_RECV_PACKET_POOL_SIZE,sizeof(NPROT_RECV_PACKET_RSVD));
if (Status != NDIS_STATUS_SUCCESS) {
DEBUGP(DL_WARN, ("CreateBinding: failed to alloc recv packet pool: %x\n", Status));
break;
};
// 包池。用来做接收缓冲区。
NdisAllocateBufferPool(&Status,&pOpenContext->RecvBufferPool,MAX_RECV_PACKET_POOL_SIZE);
if (Status != NDIS_STATUS_SUCCESS) {
DEBUGP(DL_WARN, ("CreateBinding: failed to alloc recv buffer pool: %x\n", Status));
break;
};
// 电源状态是打开着的。
pOpenContext->PowerState = NetDeviceStateD0;
// 初始化一个打开事件。(打开就是绑定!)
NPROT_INIT_EVENT(&pOpenContext->BindEvent);
NdisOpenAdapter(&Status,&OpenErrorCode,&pOpenContext->BindingHandle,&SelectedMediumIndex,&MediumArray[0],sizeof(MediumArray) / sizeof(NDIS_MEDIUM),Globals.NdisProtocolHandle,(NDIS_HANDLE)pOpenContext,&pOpenContext->DeviceName,0,NULL);
// 等待请求完成。
if (Status == NDIS_STATUS_PENDING) {
NPROT_WAIT_EVENT(&pOpenContext->BindEvent, 0);
Status = pOpenContext->BindStatus;
};
// 如果不成功
if (Status != NDIS_STATUS_SUCCESS) {
DEBUGP(DL_WARN, ("CreateBinding: NdisOpenAdapter (%ws) failed: %x\n", pOpenContext->DeviceName.Buffer, Status));
break;
};
// 记住我们已经成功的绑定了。但是我们还没有更新打开状态。这是为了避免别的线程开始关闭这个绑定。
fOpenComplete = TRUE;
// 发请求,获得一个可阅读的名字。不过这并不是非成功不可的。所以不检查返回值。
(VOID)NdisQueryAdapterInstanceName(&pOpenContext->DeviceDescr,pOpenContext->BindingHandle);
// 获得下面网卡的Mac地址
Status = ndisprotDoRequest(pOpenContext,NdisRequestQueryInformation,OID_802_3_CURRENT_ADDRESS,&pOpenContext->CurrentAddress[0],NPROT_MAC_ADDR_LEN,&BytesProcessed);
if (Status != NDIS_STATUS_SUCCESS) {
DEBUGP(DL_WARN, ("CreateBinding: qry current address failed: %x\n", Status));
break;
};
// 获得网卡选项
Status = ndisprotDoRequest(pOpenContext,NdisRequestQueryInformation,OID_GEN_MAC_OPTIONS,&pOpenContext->MacOptions,sizeof(pOpenContext->MacOptions),&BytesProcessed);
if (Status != NDIS_STATUS_SUCCESS) {
DEBUGP(DL_WARN, ("CreateBinding: qry MAC options failed: %x\n", Status));
break;
};
// 获得最大帧长
Status = ndisprotDoRequest(pOpenContext,NdisRequestQueryInformation,OID_GEN_MAXIMUM_FRAME_SIZE,&pOpenContext->MaxFrameSize,sizeof(pOpenContext->MaxFrameSize),&BytesProcessed);
if (Status != NDIS_STATUS_SUCCESS) {
DEBUGP(DL_WARN, ("CreateBinding: qry max frame failed: %x\n", Status));
break;
};
// 获得下层连接状态。
Status = ndisprotDoRequest(pOpenContext,NdisRequestQueryInformation,OID_GEN_MEDIA_CONNECT_STATUS,&GenericUlong,sizeof(GenericUlong),&BytesProcessed);
if (Status != NDIS_STATUS_SUCCESS) {
DEBUGP(DL_WARN, ("CreateBinding: qry media connect status failed: %x\n", Status));
break;
};
if (GenericUlong == NdisMediaStateConnected)
NPROT_SET_FLAGS(pOpenContext->Flags, NUIOO_MEDIA_FLAGS, NUIOO_MEDIA_CONNECTED);
else
NPROT_SET_FLAGS(pOpenContext->Flags, NUIOO_MEDIA_FLAGS, NUIOO_MEDIA_DISCONNECTED);
// 设置标记
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
NPROT_SET_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_ACTIVE);
// 检测是否这时候出现了一个解除绑定请求
if (NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_UNBIND_FLAGS, NUIOO_UNBIND_RECEIVED))
// 出现了则这次绑定失败
Status = NDIS_STATUS_FAILURE;
// 标记测试完之后就可以解锁了。
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
} while (FALSE);
// 如果没有成功,而且
if ((Status != NDIS_STATUS_SUCCESS) && !fDoNotDisturb) {
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
// 如果是已经成功的绑定了
if (fOpenComplete)
// 如果已经绑定结束了,设置已经绑定标记。
NPROT_SET_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_ACTIVE);
else if (NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_OPENING))
// 如果是正在绑定过程中,设置绑定失败了。
NPROT_SET_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_FAILED);
// 释放锁。
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
// 调用停止绑定函数。这里会释放所有资源。
ndisprotShutdownBinding(pOpenContext);
};
DEBUGP(DL_INFO, ("CreateBinding: OpenContext %p, Status %x\n",pOpenContext, Status));
return (Status);
};
VOID ndisprotShutdownBinding(IN PNDISPROT_OPEN_CONTEXT pOpenContext) {
NDIS_STATUS Status;
BOOLEAN DoCloseBinding = FALSE;
do {
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
// 检查标记,如果是正在打开的话,立刻退出。放弃解除绑定操作。
if (NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_OPENING)) {
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
break;
};
// 如果是绑定已经完成的情况,则设置为开始解除绑定。其他的情况则根本不用做下面的操作。因为绑定既然没有完成,也当然不用解除绑定。
if (NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_ACTIVE)) {
NPROT_SET_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_CLOSING);
DoCloseBinding = TRUE;
};
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
if (DoCloseBinding) {
// 从这里开始解除绑定。
ULONG PacketFilter = 0;
ULONG BytesRead = 0;
// 把绑定了的网卡的包过滤器设置为0,换句话说,就是从现在开始停止收包。这是为了便于清理资源。
Status = ndisprotDoRequest(pOpenContext, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &PacketFilter, sizeof(PacketFilter), &BytesRead);
if (Status != NDIS_STATUS_SUCCESS)
DEBUGP(DL_WARN, ("ShutDownBinding: set packet filter failed: %x\n", Status));
// 把这个网卡的广播列表设置为NULL.
Status = ndisprotDoRequest(pOpenContext, NdisRequestSetInformation, OID_802_3_MULTICAST_LIST, NULL, 0, &BytesRead);
if (Status != NDIS_STATUS_SUCCESS)
DEBUGP(DL_WARN, ("ShutDownBinding: set multicast list failed: %x\n", Status));
// 取消所有的提交状态IRP。
ndisServiceIndicateStatusIrp(pOpenContext, 0, NULL, 0, TRUE);
// 等待所有未决IRP完成。
ndisprotWaitForPendingIO(pOpenContext, TRUE);
// 清理掉接收队列中所有的包。
ndisprotFlushReceiveQueue(pOpenContext);
// 初始化绑定事件,这个时间将用来等待解除绑定完成。
NPROT_INIT_EVENT(&pOpenContext->BindEvent);
DEBUGP(DL_INFO, ("ShutdownBinding: Closing OpenContext %p, BindingHandle %p\n", pOpenContext, pOpenContext->BindingHandle));
// 正式调用解除绑定。
NdisCloseAdapter(&Status, pOpenContext->BindingHandle);
if (Status == NDIS_STATUS_PENDING) {
NPROT_WAIT_EVENT(&pOpenContext->BindEvent, 0);
Status = pOpenContext->BindStatus;
};
NPROT_ASSERT(Status == NDIS_STATUS_SUCCESS);
pOpenContext->BindingHandle = NULL;
};
if (DoCloseBinding) {
// 设置上已经解除绑定的标记。
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
NPROT_SET_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_IDLE);
NPROT_SET_FLAGS(pOpenContext->Flags, NUIOO_UNBIND_FLAGS, 0);
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
};
// 下面的操作主要是释放资源。
NPROT_ACQUIRE_LOCK(&Globals.GlobalLock);
NPROT_REMOVE_ENTRY_LIST(&pOpenContext->Link);
NPROT_RELEASE_LOCK(&Globals.GlobalLock);
ndisprotFreeBindResources(pOpenContext);
NPROT_DEREF_OPEN(pOpenContext); // Shutdown binding
} while (FALSE);
return;
};
VOID
ndisprotFreeBindResources(
IN PNDISPROT_OPEN_CONTEXT pOpenContext
)
/*++
Routine Description:
Free any resources set up for an NDIS binding.
Arguments:
pOpenContext - pointer to open context block
Return Value:
None
--*/
{
if (pOpenContext->SendPacketPool != NULL) {
NdisFreePacketPool(pOpenContext->SendPacketPool);
pOpenContext->SendPacketPool = NULL;
}
if (pOpenContext->RecvPacketPool != NULL) {
NdisFreePacketPool(pOpenContext->RecvPacketPool);
pOpenContext->RecvPacketPool = NULL;
}
if (pOpenContext->RecvBufferPool != NULL) {
NdisFreeBufferPool(pOpenContext->RecvBufferPool);
pOpenContext->RecvBufferPool = NULL;
}
if (pOpenContext->SendBufferPool != NULL) {
NdisFreeBufferPool(pOpenContext->SendBufferPool);
pOpenContext->SendBufferPool = NULL;
}
if (pOpenContext->DeviceName.Buffer != NULL) {
NPROT_FREE_MEM(pOpenContext->DeviceName.Buffer);
pOpenContext->DeviceName.Buffer = NULL;
pOpenContext->DeviceName.Length =
pOpenContext->DeviceName.MaximumLength = 0;
}
if (pOpenContext->DeviceDescr.Buffer != NULL) {
// this would have been allocated by NdisQueryAdpaterInstanceName.
NdisFreeMemory(pOpenContext->DeviceDescr.Buffer, 0, 0);
pOpenContext->DeviceDescr.Buffer = NULL;
}
}
VOID
ndisprotWaitForPendingIO(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN BOOLEAN DoCancelReads
)
/*++
Routine Description:
Utility function to wait for all outstanding I/O to complete
on an open context. It is assumed that the open context
won't go away while we are in this routine.
Arguments:
pOpenContext - pointer to open context structure
DoCancelReads - do we wait for pending reads to go away (and cancel them)?
Return Value:
None
--*/
{
NDIS_STATUS Status;
ULONG LoopCount;
ULONG PendingCount;
#ifdef NDIS51
// Wait for any pending sends or requests on the binding to complete.
for (LoopCount = 0; LoopCount < 60; LoopCount++) {
Status = NdisQueryPendingIOCount(
pOpenContext->BindingHandle,
&PendingCount);
if ((Status != NDIS_STATUS_SUCCESS) ||
(PendingCount == 0)) {
break;
}
DEBUGP(DL_INFO, ("WaitForPendingIO: Open %p, %d pending I/O at NDIS\n",
pOpenContext, PendingCount));
NPROT_SLEEP(2);
}
NPROT_ASSERT(LoopCount < 60);
#endif // NDIS51
// Make sure any threads trying to send have finished.
for (LoopCount = 0; LoopCount < 60; LoopCount++) {
if (pOpenContext->PendedSendCount == 0) {
break;
}
DEBUGP(DL_WARN, ("WaitForPendingIO: Open %p, %d pended sends\n",
pOpenContext, pOpenContext->PendedSendCount));
NPROT_SLEEP(1);
}
NPROT_ASSERT(LoopCount < 60);
if (DoCancelReads) {
// Wait for any pended reads to complete/cancel.
while (pOpenContext->PendedReadCount != 0) {
DEBUGP(DL_INFO, ("WaitForPendingIO: Open %p, %d pended reads\n",
pOpenContext, pOpenContext->PendedReadCount));
// Cancel any pending reads.
ndisprotCancelPendingReads(pOpenContext);
NPROT_SLEEP(1);
}
}
}
VOID
ndisprotDoProtocolUnload(
VOID
)
/*++
Routine Description:
Utility routine to handle unload from the NDIS protocol side.
Arguments:
None
Return Value:
None
--*/
{
NDIS_HANDLE ProtocolHandle;
NDIS_STATUS Status;
DEBUGP(DL_INFO, ("ProtocolUnload: ProtocolHandle %lx\n",
Globals.NdisProtocolHandle));
if (Globals.NdisProtocolHandle != NULL) {
ProtocolHandle = Globals.NdisProtocolHandle;
Globals.NdisProtocolHandle = NULL;
NdisDeregisterProtocol(
&Status,
ProtocolHandle
);
}
}
NDIS_STATUS ndisprotDoRequest(IN PNDISPROT_OPEN_CONTEXT pOpenContext, IN NDIS_REQUEST_TYPE RequestType, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG pBytesProcessed) {
NDISPROT_REQUEST ReqContext;
PNDIS_REQUEST pNdisRequest = &ReqContext.Request;
NDIS_STATUS Status;
// 初始化一个事件。这个事件会在请求完成函数中被设置,以便通知请求完成了。
NPROT_INIT_EVENT(&ReqContext.ReqEvent);
// 请求的类型。如果只是查询信息,只要用NdisRequestQueryInformation就可以了。
pNdisRequest->RequestType = RequestType;
// 根据不同的请求类型,填写OID和输入输出缓冲区。
switch (RequestType) {
case NdisRequestQueryInformation:
{
pNdisRequest->DATA.QUERY_INFORMATION.Oid = Oid;
pNdisRequest->DATA.QUERY_INFORMATION.InformationBuffer = InformationBuffer;
pNdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength = InformationBufferLength;
break;
};
case NdisRequestSetInformation:
{
pNdisRequest->DATA.SET_INFORMATION.Oid = Oid;
pNdisRequest->DATA.SET_INFORMATION.InformationBuffer = InformationBuffer;
pNdisRequest->DATA.SET_INFORMATION.InformationBufferLength = InformationBufferLength;
break;
};
default:
{
NPROT_ASSERT(FALSE);
break;
};
};
// 发送请求
NdisRequest(&Status, pOpenContext->BindingHandle, pNdisRequest);
// 如果是未决,则等待事件。这个事件会在完成函数中设置。
if (Status == NDIS_STATUS_PENDING) {
NPROT_WAIT_EVENT(&ReqContext.ReqEvent, 0);
Status = ReqContext.Status;
};
// 如果成功了的话...
if (Status == NDIS_STATUS_SUCCESS) {
// 获得结果的长度。这个结果的长度是实际需要的长度。可能比我们实际提供的长度要长。
*pBytesProcessed = (RequestType == NdisRequestQueryInformation) ? pNdisRequest->DATA.QUERY_INFORMATION.BytesWritten : pNdisRequest->DATA.SET_INFORMATION.BytesRead;
// 如果结果长度比实际上我们提供的缓冲区要长,那么就简单的设置为输入参数中缓冲区的最大长度。
if (*pBytesProcessed > InformationBufferLength)
*pBytesProcessed = InformationBufferLength;
};
return Status;
};
NDIS_STATUS
ndisprotValidateOpenAndDoRequest(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN NDIS_REQUEST_TYPE RequestType,
IN NDIS_OID Oid,
IN PVOID InformationBuffer,
IN ULONG InformationBufferLength,
OUT PULONG pBytesProcessed,
IN BOOLEAN bWaitForPowerOn
)
/*++
Routine Description:
Utility routine to prevalidate and reference an open context
before calling ndisprotDoRequest. This routine makes sure
we have a valid binding.
Arguments:
pOpenContext - pointer to our open context
RequestType - NdisRequest[Set|Query]Information
Oid - the object being set/queried
InformationBuffer - data for the request
InformationBufferLength - length of the above
pBytesProcessed - place to return bytes read/written
bWaitForPowerOn - Wait for the device to be powered on if it isn't already.
Return Value:
Status of the set/query request
--*/
{
NDIS_STATUS Status;
do {
if (pOpenContext == NULL) {
DEBUGP(DL_WARN, ("ValidateOpenAndDoRequest: request on unassociated file object!\n"));
Status = NDIS_STATUS_INVALID_DATA;
break;
}
NPROT_STRUCT_ASSERT(pOpenContext, oc);
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
// Proceed only if we have a binding.
if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_ACTIVE)) {
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
Status = NDIS_STATUS_INVALID_DATA;
break;
}
NPROT_ASSERT(pOpenContext->BindingHandle != NULL);
// Make sure that the binding does not go away until we
// are finished with the request.
NdisInterlockedIncrement((PLONG)&pOpenContext->PendedSendCount);
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
if (bWaitForPowerOn) {
// Wait for the device below to be powered up.
// We don't wait indefinitely here - this is to avoid
// a PROCESS_HAS_LOCKED_PAGES bugcheck that could happen
// if the calling process terminates, and this IRP doesn't
// complete within a reasonable time. An alternative would
// be to explicitly handle cancellation of this IRP.
NPROT_WAIT_EVENT(&pOpenContext->PoweredUpEvent, 4500);
}
Status = ndisprotDoRequest(
pOpenContext,
RequestType,
Oid,
InformationBuffer,
InformationBufferLength,
pBytesProcessed);
// Let go of the binding.
NdisInterlockedDecrement((PLONG)&pOpenContext->PendedSendCount);
} while (FALSE);
DEBUGP(DL_LOUD, ("ValidateOpenAndDoReq: Open %p/%x, OID %x, Status %x\n",
pOpenContext, pOpenContext->Flags, Oid, Status));
return (Status);
}
VOID
NdisProtResetComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status
)
/*++
Routine Description:
NDIS entry point indicating that a protocol initiated reset
has completed. Since we never call NdisReset(), this should
never be called.
Arguments:
ProtocolBindingContext - pointer to open context
Status - status of reset completion
Return Value:
None
--*/
{
UNREFERENCED_PARAMETER(ProtocolBindingContext);
UNREFERENCED_PARAMETER(Status);
ASSERT(FALSE);
return;
}
VOID NdisProtRequestComplete(IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_REQUEST pNdisRequest, IN NDIS_STATUS Status) {
PNDISPROT_OPEN_CONTEXT pOpenContext;
PNDISPROT_REQUEST pReqContext;
// 这两句话起验证的作用,确保输入参数ProtocolBindingContext是合法的。但是对后面的处理没影响。
pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext;
NPROT_STRUCT_ASSERT(pOpenContext, oc);
// 从pNdisRequest中得到请求上下文
pReqContext = CONTAINING_RECORD(pNdisRequest, NDISPROT_REQUEST, Request);
// 保存结果状态
pReqContext->Status = Status;
// 设置事件
NPROT_SIGNAL_EVENT(&pReqContext->ReqEvent);
return;
};
VOID
NdisProtStatus(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS GeneralStatus,
__in_bcount(StatusBufferSize) IN PVOID StatusBuffer,
IN UINT StatusBufferSize
)
/*++
Routine Description:
Protocol entry point called by NDIS to indicate a change
in status at the miniport.
We make note of reset and media connect status indications.
Arguments:
ProtocolBindingContext - pointer to open context
GeneralStatus - status code
StatusBuffer - status-specific additional information
StatusBufferSize - size of the above
Return Value:
None
--*/
{
PNDISPROT_OPEN_CONTEXT pOpenContext;
UNREFERENCED_PARAMETER(StatusBuffer);
UNREFERENCED_PARAMETER(StatusBufferSize);
pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext;
NPROT_STRUCT_ASSERT(pOpenContext, oc);
DEBUGP(DL_INFO, ("Status: Open %p, Status %x\n",
pOpenContext, GeneralStatus));
ndisServiceIndicateStatusIrp(pOpenContext,
GeneralStatus,
StatusBuffer,
StatusBufferSize,
FALSE);
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
do {
if (pOpenContext->PowerState != NetDeviceStateD0) {
// The device is in a low power state.
DEBUGP(DL_INFO, ("Status: Open %p in power state %d,"
" Status %x ignored\n", pOpenContext,
pOpenContext->PowerState, GeneralStatus));
// We continue and make note of status indications
// break;
// NOTE that any actions we take based on these
// status indications should take into account
// the current device power state.
}
switch (GeneralStatus) {
case NDIS_STATUS_RESET_START:
NPROT_ASSERT(!NPROT_TEST_FLAGS(pOpenContext->Flags,
NUIOO_RESET_FLAGS,
NUIOO_RESET_IN_PROGRESS));
NPROT_SET_FLAGS(pOpenContext->Flags,
NUIOO_RESET_FLAGS,
NUIOO_RESET_IN_PROGRESS);
break;
case NDIS_STATUS_RESET_END:
NPROT_ASSERT(NPROT_TEST_FLAGS(pOpenContext->Flags,
NUIOO_RESET_FLAGS,
NUIOO_RESET_IN_PROGRESS));
NPROT_SET_FLAGS(pOpenContext->Flags,
NUIOO_RESET_FLAGS,
NUIOO_NOT_RESETTING);
break;
case NDIS_STATUS_MEDIA_CONNECT:
NPROT_SET_FLAGS(pOpenContext->Flags,
NUIOO_MEDIA_FLAGS,
NUIOO_MEDIA_CONNECTED);
break;
case NDIS_STATUS_MEDIA_DISCONNECT:
NPROT_SET_FLAGS(pOpenContext->Flags,
NUIOO_MEDIA_FLAGS,
NUIOO_MEDIA_DISCONNECTED);
break;
default:
break;
}
} while (FALSE);
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
}
VOID
NdisProtStatusComplete(
IN NDIS_HANDLE ProtocolBindingContext
)
/*++
Routine Description:
Protocol entry point called by NDIS. We ignore this.
Arguments:
ProtocolBindingContext - pointer to open context
Return Value:
None
--*/
{
PNDISPROT_OPEN_CONTEXT pOpenContext;
pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext;
NPROT_STRUCT_ASSERT(pOpenContext, oc);
return;
}
NDIS_STATUS
ndisprotQueryBinding(
IN PUCHAR pBuffer,
IN ULONG InputLength,
IN ULONG OutputLength,
OUT PULONG pBytesReturned
)
/*++
Routine Description:
Return information about the specified binding.
Arguments:
pBuffer - pointer to NDISPROT_QUERY_BINDING
InputLength - input buffer size
OutputLength - output buffer size
pBytesReturned - place to return copied byte count.
Return Value:
NDIS_STATUS_SUCCESS if successful, failure code otherwise.
--*/
{
PNDISPROT_QUERY_BINDING pQueryBinding;
PNDISPROT_OPEN_CONTEXT pOpenContext;
PLIST_ENTRY pEnt;
ULONG Remaining;
ULONG BindingIndex;
NDIS_STATUS Status;
do {
if (InputLength < sizeof(NDISPROT_QUERY_BINDING)) {
Status = NDIS_STATUS_RESOURCES;
break;
}
if (OutputLength < sizeof(NDISPROT_QUERY_BINDING)) {
Status = NDIS_STATUS_BUFFER_OVERFLOW;
break;
}
Remaining = OutputLength - sizeof(NDISPROT_QUERY_BINDING);
pQueryBinding = (PNDISPROT_QUERY_BINDING)pBuffer;
BindingIndex = pQueryBinding->BindingIndex;
Status = NDIS_STATUS_ADAPTER_NOT_FOUND;
pOpenContext = NULL;
NPROT_ACQUIRE_LOCK(&Globals.GlobalLock);
for (pEnt = Globals.OpenList.Flink;
pEnt != &Globals.OpenList;
pEnt = pEnt->Flink) {
pOpenContext = CONTAINING_RECORD(pEnt, NDISPROT_OPEN_CONTEXT, Link);
NPROT_STRUCT_ASSERT(pOpenContext, oc);
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
// Skip if not bound.
if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_ACTIVE)) {
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
continue;
}
if (BindingIndex == 0) {
// Got the binding we are looking for. Copy the device
// name and description strings to the output buffer.
DEBUGP(DL_INFO,
("QueryBinding: found open %p\n", pOpenContext));
pQueryBinding->DeviceNameLength = pOpenContext->DeviceName.Length + sizeof(WCHAR);
pQueryBinding->DeviceDescrLength = pOpenContext->DeviceDescr.Length + sizeof(WCHAR);
if (Remaining < pQueryBinding->DeviceNameLength +
pQueryBinding->DeviceDescrLength) {
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
Status = NDIS_STATUS_BUFFER_OVERFLOW;
break;
}
NPROT_ZERO_MEM((PUCHAR)pBuffer + sizeof(NDISPROT_QUERY_BINDING),
pQueryBinding->DeviceNameLength +
pQueryBinding->DeviceDescrLength);
pQueryBinding->DeviceNameOffset = sizeof(NDISPROT_QUERY_BINDING);
NPROT_COPY_MEM((PUCHAR)pBuffer + pQueryBinding->DeviceNameOffset,
pOpenContext->DeviceName.Buffer,
pOpenContext->DeviceName.Length);
pQueryBinding->DeviceDescrOffset = pQueryBinding->DeviceNameOffset +
pQueryBinding->DeviceNameLength;
NPROT_COPY_MEM((PUCHAR)pBuffer + pQueryBinding->DeviceDescrOffset,
pOpenContext->DeviceDescr.Buffer,
pOpenContext->DeviceDescr.Length);
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
*pBytesReturned = pQueryBinding->DeviceDescrOffset + pQueryBinding->DeviceDescrLength;
Status = NDIS_STATUS_SUCCESS;
break;
}
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
BindingIndex--;
}
NPROT_RELEASE_LOCK(&Globals.GlobalLock);
} while (FALSE);
return (Status);
}
PNDISPROT_OPEN_CONTEXT
ndisprotLookupDevice(
__in_bcount(BindingInfoLength) IN PUCHAR pBindingInfo,
IN ULONG BindingInfoLength
)
/*++
Routine Description:
Search our global list for an open context structure that
has a binding to the specified device, and return a pointer
to it.
NOTE: we reference the open that we return.
Arguments:
pBindingInfo - pointer to unicode device name string
BindingInfoLength - length in bytes of the above.
Return Value:
Pointer to the matching open context if found, else NULL
--*/
{
PNDISPROT_OPEN_CONTEXT pOpenContext;
PLIST_ENTRY pEnt;
pOpenContext = NULL;
NPROT_ACQUIRE_LOCK(&Globals.GlobalLock);
for (pEnt = Globals.OpenList.Flink;
pEnt != &Globals.OpenList;
pEnt = pEnt->Flink) {
pOpenContext = CONTAINING_RECORD(pEnt, NDISPROT_OPEN_CONTEXT, Link);
NPROT_STRUCT_ASSERT(pOpenContext, oc);
// Check if this has the name we are looking for.
if ((pOpenContext->DeviceName.Length == BindingInfoLength) &&
NPROT_MEM_CMP(pOpenContext->DeviceName.Buffer, pBindingInfo, BindingInfoLength)) {
NPROT_REF_OPEN(pOpenContext); // ref added by LookupDevice
break;
}
pOpenContext = NULL;
}
NPROT_RELEASE_LOCK(&Globals.GlobalLock);
return (pOpenContext);
}
NDIS_STATUS
ndisprotQueryOidValue(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
OUT PVOID pDataBuffer,
IN ULONG BufferLength,
OUT PULONG pBytesWritten
)
/*++
Routine Description:
Query an arbitrary OID value from the miniport.
Arguments:
pOpenContext - pointer to open context representing our binding to the miniport
pDataBuffer - place to store the returned value
BufferLength - length of the above
pBytesWritten - place to return length returned
Return Value:
NDIS_STATUS_SUCCESS if we successfully queried the OID.
NDIS_STATUS_XXX error code otherwise.
--*/
{
NDIS_STATUS Status;
PNDISPROT_QUERY_OID pQuery;
NDIS_OID Oid;
Oid = 0;
do {
if (BufferLength < sizeof(NDISPROT_QUERY_OID)) {
Status = NDIS_STATUS_BUFFER_TOO_SHORT;
break;
}
pQuery = (PNDISPROT_QUERY_OID)pDataBuffer;
Oid = pQuery->Oid;
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_ACTIVE)) {
DEBUGP(DL_WARN,
("QueryOid: Open %p/%x is in invalid state\n",
pOpenContext, pOpenContext->Flags));
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
Status = NDIS_STATUS_FAILURE;
break;
}
// Make sure the binding doesn't go away.
NdisInterlockedIncrement((PLONG)&pOpenContext->PendedSendCount);
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
Status = ndisprotDoRequest(
pOpenContext,
NdisRequestQueryInformation,
Oid,
&pQuery->Data[0],
BufferLength - FIELD_OFFSET(NDISPROT_QUERY_OID, Data),
pBytesWritten);
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
NdisInterlockedDecrement((PLONG)&pOpenContext->PendedSendCount);
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
if (Status == NDIS_STATUS_SUCCESS) {
*pBytesWritten += FIELD_OFFSET(NDISPROT_QUERY_OID, Data);
}
} while (FALSE);
DEBUGP(DL_LOUD, ("QueryOid: Open %p/%x, OID %x, Status %x\n",
pOpenContext, pOpenContext->Flags, Oid, Status));
return (Status);
}
NDIS_STATUS
ndisprotSetOidValue(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
OUT PVOID pDataBuffer,
IN ULONG BufferLength
)
/*++
Routine Description:
Set an arbitrary OID value to the miniport.
Arguments:
pOpenContext - pointer to open context representing our binding to the miniport
pDataBuffer - buffer that contains the value to be set
BufferLength - length of the above
Return Value:
NDIS_STATUS_SUCCESS if we successfully set the OID
NDIS_STATUS_XXX error code otherwise.
--*/
{
NDIS_STATUS Status;
PNDISPROT_SET_OID pSet;
NDIS_OID Oid;
ULONG BytesWritten;
Oid = 0;
do {
if (BufferLength < sizeof(NDISPROT_SET_OID)) {
Status = NDIS_STATUS_BUFFER_TOO_SHORT;
break;
}
pSet = (PNDISPROT_SET_OID)pDataBuffer;
Oid = pSet->Oid;
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_ACTIVE)) {
DEBUGP(DL_WARN,
("SetOid: Open %p/%x is in invalid state\n",
pOpenContext, pOpenContext->Flags));

NPROT_RELEASE_LOCK(&pOpenContext->Lock);
Status = NDIS_STATUS_FAILURE;
break;
}
// Make sure the binding doesn't go away.
NdisInterlockedIncrement((PLONG)&pOpenContext->PendedSendCount);
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
Status = ndisprotDoRequest(
pOpenContext,
NdisRequestSetInformation,
Oid,
&pSet->Data[0],
BufferLength - FIELD_OFFSET(NDISPROT_SET_OID, Data),
&BytesWritten);
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
NdisInterlockedDecrement((PLONG)&pOpenContext->PendedSendCount);
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
} while (FALSE);
DEBUGP(DL_LOUD, ("SetOid: Open %p/%x, OID %x, Status %x\n",
pOpenContext, pOpenContext->Flags, Oid, Status));
return (Status);
}
NTSTATUS
ndisprotQueueStatusIndicationIrp(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN PIRP pIrp,
OUT PULONG pBytesReturned
)
/*++
Routine Description:
Queue the IRP in the context structure. This IRP will be completed
one of the following occurs: 1) Status indication is received by
the bound miniport, 2) IRP is cancelled by the sender, 3) We have
been asked to unbind from the miniport either because the device is removed
or user unchecked our binding in the network connection applet.
Arguments:
pOpenContext - pointer to open context representing our binding to the miniport
Return Value:
NTSTATUS code
--*/
{
NTSTATUS NtStatus;
DEBUGP(DL_LOUD, ("-->ndisprotQueueStatusIndicationIrp\n"));
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
if (pOpenContext->StatusIndicationIrp) {
DEBUGP(DL_WARN, ("Control IRP to indicate status is already pending: %p\n", pIrp));
NtStatus = STATUS_DEVICE_BUSY;
}
else {
// Since we are queueing the IRP, we should set the cancel routine.
IoSetCancelRoutine(pIrp, ndisCancelIndicateStatusIrp);
// Let us check to see if the IRP is cancelled at this point.
if (pIrp->Cancel && IoSetCancelRoutine(pIrp, NULL)) {
// The IRP has been already cancelled but the I/O manager
// hasn't called the cancel routine yet. So complete the
// IRP after releasing the lock.
NtStatus = STATUS_CANCELLED;
}
else {
// Queue the IRP and return status pending.
IoMarkIrpPending(pIrp);
pOpenContext->StatusIndicationIrp = pIrp;
NtStatus = STATUS_PENDING;
// The IRP shouldn't be accessed after the lock is released
// It could be grabbed by another thread or the cancel routine
// is running.
}
}
*pBytesReturned = 0;
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
DEBUGP(DL_LOUD, ("<--ndisprotQueueStatusIndicationIrp\n"));
return NtStatus;
}
VOID
ndisServiceIndicateStatusIrp(
IN PNDISPROT_OPEN_CONTEXT OpenContext,
IN NDIS_STATUS GeneralStatus,
__in_bcount_opt(StatusBufferSize) IN PVOID StatusBuffer,
IN UINT StatusBufferSize,
IN BOOLEAN Cancel
)
/*++
Routine Description:
We process the IRP based on the input arguments and complete
the IRP. If the IRP was cancelled for some reason we will let
the cancel routine do the IRP completion.
Arguments:
ProtocolBindingContext - pointer to open context
GeneralStatus - status code
StatusBuffer - status-specific additional information
StatusBufferSize - size of the above
Cancel - Should the IRP be cancelled right away.
Return Value:
None
--*/
{
PIRP pIrp = NULL;
PIO_STACK_LOCATION pIrpSp = NULL;
PNDISPROT_INDICATE_STATUS pIndicateStatus = NULL;
NTSTATUS ntStatus;
ULONG bytes = 0;
ULONG inBufLength, outBufLength;
DEBUGP(DL_LOUD, ("-->ndisServiceIndicateStatusIrp\n"));
NPROT_ACQUIRE_LOCK(&OpenContext->Lock);
pIrp = OpenContext->StatusIndicationIrp;
do {
if (pIrp) {
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pIndicateStatus = pIrp->AssociatedIrp.SystemBuffer;
inBufLength = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
outBufLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
// Clear the cancel routine.
if (IoSetCancelRoutine(pIrp, NULL)) {
// Cancel routine cannot run now and cannot have already
// started to run.
ntStatus = STATUS_CANCELLED;
if (!Cancel) {
// Check to see whether the buffer is large enough.
if (outBufLength >= sizeof(NDISPROT_INDICATE_STATUS) &&
(outBufLength - sizeof(NDISPROT_INDICATE_STATUS)) >= StatusBufferSize) {
pIndicateStatus->IndicatedStatus = GeneralStatus;
pIndicateStatus->StatusBufferLength = StatusBufferSize;
pIndicateStatus->StatusBufferOffset = sizeof(NDISPROT_INDICATE_STATUS);
NPROT_COPY_MEM((PUCHAR)pIndicateStatus +
pIndicateStatus->StatusBufferOffset,
StatusBuffer,
StatusBufferSize);
ntStatus = STATUS_SUCCESS;
}
else {
ntStatus = STATUS_BUFFER_OVERFLOW;
}
}
// Since we are completing the IRP below, clear this field.
OpenContext->StatusIndicationIrp = NULL;
// Number of bytes copied or number of bytes required.
bytes = sizeof(NDISPROT_INDICATE_STATUS) + StatusBufferSize;
break;
}
else {
// Cancel rotuine is running. Leave the irp alone.
pIrp = NULL;
}
}
} while (FALSE);
NPROT_RELEASE_LOCK(&OpenContext->Lock);
if (pIrp) {
pIrp->IoStatus.Information = bytes;
pIrp->IoStatus.Status = ntStatus;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
}
DEBUGP(DL_LOUD, ("<--ndisServiceIndicateStatusIrp\n"));
return;
}
VOID
ndisCancelIndicateStatusIrp(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
/*++
Routine Description:
Cancel the pending status indication IRP registered by the app or
another driver.
Arguments:
pDeviceObject - pointer to our device object
pIrp - IRP to be cancelled
Return Value:
None
--*/
{
PNDISPROT_OPEN_CONTEXT pOpenContext;
PIO_STACK_LOCATION pIrpSp = NULL;
UNREFERENCED_PARAMETER(pDeviceObject);
DEBUGP(DL_LOUD, ("-->ndisCancelIndicateStatusIrp\n"));
IoReleaseCancelSpinLock(pIrp->CancelIrql);
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pOpenContext = (PNDISPROT_OPEN_CONTEXT)pIrpSp->FileObject->FsContext;
NPROT_STRUCT_ASSERT(pOpenContext, oc);
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
pOpenContext->StatusIndicationIrp = NULL;
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
DEBUGP(DL_LOUD, ("<--ndisCancelIndicateStatusIrp\n"));
return;
}

ndisprot.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
ndisprot.h
Abstract:
Data structures, defines and function prototypes for NDISPROT.
Environment:
Kernel mode only.
Revision History:
--*/
#ifndef __NDISPROT__H
#define __NDISPROT__H
#define NT_DEVICE_NAME L"\\Device\\NdisProt"
#define DOS_DEVICE_NAME L"\\DosDevices\\NdisProt"
// Abstract types
typedef NDIS_EVENT NPROT_EVENT;
#define NPROT_MAC_ADDR_LEN 6
// The Open Context represents an open of our device object.
// We allocate this on processing a BindAdapter from NDIS,
// and free it when all references (see below) to it are gone.
// Binding/unbinding to an NDIS device:
// On processing a BindAdapter call from NDIS, we set up a binding
// to the specified NDIS device (miniport). This binding is
// torn down when NDIS asks us to Unbind by calling
// our UnbindAdapter handler.
// Receiving data:
// While an NDIS binding exists, read IRPs are queued on this
// structure, to be processed when packets are received.
// If data arrives in the absense of a pended read IRP, we
// queue it, to the extent of one packet, i.e. we save the
// contents of the latest packet received. We fail read IRPs
// received when no NDIS binding exists (or is in the process
// of being torn down).
// Sending data:
// Write IRPs are used to send data. Each write IRP maps to
// a single NDIS packet. Packet send-completion is mapped to
// write IRP completion. We use NDIS 5.1 CancelSend to support
// write IRP cancellation. Write IRPs that arrive when we don't
// have an active NDIS binding are failed.
// Reference count:
// The following are long-lived references:
// OPEN_DEVICE ioctl (goes away on processing a Close IRP)
// Pended read IRPs
// Queued received packets
// Uncompleted write IRPs (outstanding sends)
// Existence of NDIS binding
typedef struct _NDISPROT_OPEN_CONTEXT {
LIST_ENTRY Link; // Link into global list
ULONG Flags; // State information
ULONG RefCount;
NPROT_LOCK Lock;
PFILE_OBJECT pFileObject; // Set on OPEN_DEVICE
NDIS_HANDLE BindingHandle;
NDIS_HANDLE SendPacketPool;
NDIS_HANDLE SendBufferPool;
NDIS_HANDLE RecvPacketPool;
NDIS_HANDLE RecvBufferPool;
ULONG MacOptions;
ULONG MaxFrameSize;
LIST_ENTRY PendedWrites; // pended Write IRPs
ULONG PendedSendCount;
LIST_ENTRY PendedReads; // pended Read IRPs
ULONG PendedReadCount;
LIST_ENTRY RecvPktQueue; // queued rcv packets
ULONG RecvPktCount;
NET_DEVICE_POWER_STATE PowerState;
NDIS_EVENT PoweredUpEvent; // signalled iff PowerState is D0
NDIS_STRING DeviceName; // used in NdisOpenAdapter
NDIS_STRING DeviceDescr; // friendly name
NDIS_STATUS BindStatus; // for Open/CloseAdapter
NPROT_EVENT BindEvent; // for Open/CloseAdapter
BOOLEAN bRunningOnWin9x;// TRUE if Win98/SE/ME, FALSE if NT
ULONG oc_sig; // Signature for sanity
UCHAR CurrentAddress[NPROT_MAC_ADDR_LEN];
PIRP StatusIndicationIrp;
} NDISPROT_OPEN_CONTEXT, * PNDISPROT_OPEN_CONTEXT;
#define oc_signature 'OiuN'
// Definitions for Flags above.
#define NUIOO_BIND_IDLE 0x00000000
#define NUIOO_BIND_OPENING 0x00000001
#define NUIOO_BIND_FAILED 0x00000002
#define NUIOO_BIND_ACTIVE 0x00000004
#define NUIOO_BIND_CLOSING 0x00000008
#define NUIOO_BIND_FLAGS 0x0000000F // State of the binding
#define NUIOO_OPEN_IDLE 0x00000000
#define NUIOO_OPEN_ACTIVE 0x00000010
#define NUIOO_OPEN_FLAGS 0x000000F0 // State of the I/O open
#define NUIOO_RESET_IN_PROGRESS 0x00000100
#define NUIOO_NOT_RESETTING 0x00000000
#define NUIOO_RESET_FLAGS 0x00000100
#define NUIOO_MEDIA_CONNECTED 0x00000000
#define NUIOO_MEDIA_DISCONNECTED 0x00000200
#define NUIOO_MEDIA_FLAGS 0x00000200
#define NUIOO_READ_SERVICING 0x00100000 // Is the read service
// routine running?
#define NUIOO_READ_FLAGS 0x00100000
#define NUIOO_UNBIND_RECEIVED 0x10000000 // Seen NDIS Unbind?
#define NUIOO_UNBIND_FLAGS 0x10000000
// Globals:
typedef struct _NDISPROT_GLOBALS {
PDRIVER_OBJECT pDriverObject;
PDEVICE_OBJECT ControlDeviceObject;
NDIS_HANDLE NdisProtocolHandle;
UCHAR PartialCancelId; // for cancelling sends
ULONG LocalCancelId;
LIST_ENTRY OpenList; // of OPEN_CONTEXT structures
NPROT_LOCK GlobalLock; // to protect the above
NPROT_EVENT BindsComplete; // have we seen NetEventBindsComplete?
} NDISPROT_GLOBALS, * PNDISPROT_GLOBALS;
// NDIS Request context structure
typedef struct _NDISPROT_REQUEST {
NDIS_REQUEST Request;
NPROT_EVENT ReqEvent;
ULONG Status;
} NDISPROT_REQUEST, * PNDISPROT_REQUEST;
#define NUIOO_PACKET_FILTER (NDIS_PACKET_TYPE_DIRECTED| \
NDIS_PACKET_TYPE_MULTICAST| \
NDIS_PACKET_TYPE_BROADCAST)
// Send packet pool bounds
#define MIN_SEND_PACKET_POOL_SIZE 20
#define MAX_SEND_PACKET_POOL_SIZE 400
// ProtocolReserved in sent packets. We save a pointer to the IRP
// that generated the send.
// The RefCount is used to determine when to free the packet back
// to its pool. It is used to synchronize between a thread completing
// a send and a thread attempting to cancel a send.
typedef struct _NPROT_SEND_PACKET_RSVD {
PIRP pIrp;
ULONG RefCount;
} NPROT_SEND_PACKET_RSVD, * PNPROT_SEND_PACKET_RSVD;
// Receive packet pool bounds
#define MIN_RECV_PACKET_POOL_SIZE 4
#define MAX_RECV_PACKET_POOL_SIZE 20
// Max receive packets we allow to be queued up
#define MAX_RECV_QUEUE_SIZE 4
// ProtocolReserved in received packets: we link these
// packets up in a queue waiting for Read IRPs.
typedef struct _NPROT_RECV_PACKET_RSVD {
LIST_ENTRY Link;
PNDIS_BUFFER pOriginalBuffer; // used if we had to partial-map
} NPROT_RECV_PACKET_RSVD, * PNPROT_RECV_PACKET_RSVD;
#include <pshpack1.h>
typedef struct _NDISPROT_ETH_HEADER {
UCHAR DstAddr[NPROT_MAC_ADDR_LEN];
UCHAR SrcAddr[NPROT_MAC_ADDR_LEN];
USHORT EthType;
} NDISPROT_ETH_HEADER;
typedef struct _NDISPROT_ETH_HEADER UNALIGNED* PNDISPROT_ETH_HEADER;
#include <poppack.h>
extern NDISPROT_GLOBALS Globals;
#define NPROT_ALLOC_TAG 'oiuN'
#ifndef NdisGetPoolFromPacket
#define NdisGetPoolFromPacket(_Pkt) (_Pkt->Private.Pool)
#endif
#ifndef NDIS_PACKET_FIRST_NDIS_BUFFER
#define NDIS_PACKET_FIRST_NDIS_BUFFER(_Pkt) ((_Pkt)->Private.Head)
#endif
// Prototypes.
DRIVER_INITIALIZE DriverEntry;
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath
);
DRIVER_UNLOAD NdisProtUnload;
VOID
NdisProtUnload(
IN PDRIVER_OBJECT DriverObject
);
DRIVER_DISPATCH NdisProtOpen;
NTSTATUS
NdisProtOpen(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
);
DRIVER_DISPATCH NdisProtClose;
NTSTATUS
NdisProtClose(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
);
DRIVER_DISPATCH NdisProtCleanup;
NTSTATUS
NdisProtCleanup(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
);
DRIVER_DISPATCH NdisProtIoControl;
NTSTATUS
NdisProtIoControl(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
);
NTSTATUS
ndisprotOpenDevice(
__in_bcount(DeviceNameLength) IN PUCHAR pDeviceName,
IN ULONG DeviceNameLength,
IN PFILE_OBJECT pFileObject,
OUT PNDISPROT_OPEN_CONTEXT* ppOpenContext
);
VOID
ndisprotRefOpen(
IN PNDISPROT_OPEN_CONTEXT pOpenContext
);
VOID
ndisprotDerefOpen(
IN PNDISPROT_OPEN_CONTEXT pOpenContext
);
#if DBG
VOID
ndisprotDbgRefOpen(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN ULONG FileNumber,
IN ULONG LineNumber
);
VOID
ndisprotDbgDerefOpen(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN ULONG FileNumber,
IN ULONG LineNumber
);
#endif // DBG
VOID
NdisProtBindAdapter(
OUT PNDIS_STATUS pStatus,
IN NDIS_HANDLE BindContext,
IN PNDIS_STRING DeviceName,
IN PVOID SystemSpecific1,
IN PVOID SystemSpecific2
);
VOID
NdisProtOpenAdapterComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status,
IN NDIS_STATUS OpenErrorCode
);
VOID
NdisProtUnbindAdapter(
OUT PNDIS_STATUS pStatus,
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_HANDLE UnbindContext
);
VOID
NdisProtCloseAdapterComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status
);
NDIS_STATUS
NdisProtPnPEventHandler(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNET_PNP_EVENT pNetPnPEvent
);
VOID
NdisProtProtocolUnloadHandler(
VOID
);
NDIS_STATUS
ndisprotCreateBinding(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
__in_bcount(BindingInfoLength) IN PUCHAR pBindingInfo,
IN ULONG BindingInfoLength
);
VOID
ndisprotShutdownBinding(
IN PNDISPROT_OPEN_CONTEXT pOpenContext
);
VOID
ndisprotFreeBindResources(
IN PNDISPROT_OPEN_CONTEXT pOpenContext
);
VOID
ndisprotWaitForPendingIO(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN BOOLEAN DoCancelReads
);
VOID
ndisprotDoProtocolUnload(
VOID
);
NDIS_STATUS
ndisprotDoRequest(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN NDIS_REQUEST_TYPE RequestType,
IN NDIS_OID Oid,
IN PVOID InformationBuffer,
IN ULONG InformationBufferLength,
OUT PULONG pBytesProcessed
);
NDIS_STATUS
ndisprotValidateOpenAndDoRequest(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN NDIS_REQUEST_TYPE RequestType,
IN NDIS_OID Oid,
IN PVOID InformationBuffer,
IN ULONG InformationBufferLength,
OUT PULONG pBytesProcessed,
IN BOOLEAN bWaitForPowerOn
);
VOID
NdisProtResetComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status
);
VOID
NdisProtRequestComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_REQUEST pNdisRequest,
IN NDIS_STATUS Status
);
VOID
NdisProtStatus(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS GeneralStatus,
__in_bcount(StatusBufferSize) IN PVOID StatusBuffer,
IN UINT StatusBufferSize
);
VOID
NdisProtStatusComplete(
IN NDIS_HANDLE ProtocolBindingContext
);
NDIS_STATUS
ndisprotQueryBinding(
IN PUCHAR pBuffer,
IN ULONG InputLength,
IN ULONG OutputLength,
OUT PULONG pBytesReturned
);
PNDISPROT_OPEN_CONTEXT
ndisprotLookupDevice(
__in_bcount(BindingInfoLength) IN PUCHAR pBindingInfo,
IN ULONG BindingInfoLength
);
NDIS_STATUS
ndisprotQueryOidValue(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
OUT PVOID pDataBuffer,
IN ULONG BufferLength,
OUT PULONG pBytesWritten
);
NDIS_STATUS
ndisprotSetOidValue(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
OUT PVOID pDataBuffer,
IN ULONG BufferLength
);
DRIVER_DISPATCH NdisProtRead;
NTSTATUS
NdisProtRead(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
);
DRIVER_CANCEL NdisProtCancelRead;
VOID
NdisProtCancelRead(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
);
VOID
ndisprotServiceReads(
IN PNDISPROT_OPEN_CONTEXT pOpenContext
);
NDIS_STATUS
NdisProtReceive(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_HANDLE MacReceiveContext,
__in_bcount(HeaderBufferSize) IN PVOID pHeaderBuffer,
IN UINT HeaderBufferSize,
__in_bcount(LookaheadBufferSize) IN PVOID pLookaheadBuffer,
IN UINT LookaheadBufferSize,
IN UINT PacketSize
);
VOID
NdisProtTransferDataComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET pNdisPacket,
IN NDIS_STATUS TransferStatus,
IN UINT BytesTransferred
);
VOID
NdisProtReceiveComplete(
IN NDIS_HANDLE ProtocolBindingContext
);
INT
NdisProtReceivePacket(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET pNdisPacket
);
VOID
ndisprotShutdownBinding(
IN PNDISPROT_OPEN_CONTEXT pOpenContext
);
VOID
ndisprotQueueReceivePacket(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN PNDIS_PACKET pRcvPacket
);
PNDIS_PACKET
ndisprotAllocateReceivePacket(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN UINT DataLength,
OUT PUCHAR* ppDataBuffer
);
VOID
ndisprotFreeReceivePacket(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN PNDIS_PACKET pNdisPacket
);
VOID
ndisprotCancelPendingReads(
IN PNDISPROT_OPEN_CONTEXT pOpenContext
);
VOID
ndisprotFlushReceiveQueue(
IN PNDISPROT_OPEN_CONTEXT pOpenContext
);
DRIVER_DISPATCH NdisProtWrite;
NTSTATUS
NdisProtWrite(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
);
DRIVER_CANCEL NdisProtCancelWrite;
VOID
NdisProtCancelWrite(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
);
VOID
NdisProtSendComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET pNdisPacket,
IN NDIS_STATUS Status
);
NTSTATUS
ndisprotQueueStatusIndicationIrp(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN PIRP pIrp,
OUT PULONG pBytesReturned
);
DRIVER_CANCEL ndisCancelIndicateStatusIrp;
VOID
ndisCancelIndicateStatusIrp(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
);
VOID
ndisServiceIndicateStatusIrp(
IN PNDISPROT_OPEN_CONTEXT OpenContext,
IN NDIS_STATUS GeneralStatus,
__in_bcount(StatusBufferSize) IN PVOID StatusBuffer,
IN UINT StatusBufferSize,
IN BOOLEAN Cancel
);
#ifdef EX_CALLBACK
BOOLEAN
ndisprotRegisterExCallBack();
VOID
ndisprotUnregisterExCallBack();
VOID
ndisprotCallback(
PVOID CallBackContext,
PVOID Source,
PVOID NotifyPresenceCallback
);
#else
#define ndisprotRegisterExCallBack() TRUE
#define ndisprotUnregisterExCallBack()
#endif
#endif // __NDISPROT__H

ndisp.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
/*++

Copyright (c) 2000 Microsoft Corporation

Module Name:

ntdisp.c

Abstract:

NT Entry points and dispatch routines for NDISPROT.

Environment:

Kernel mode only.

Revision History:

--*/

#include "precomp.h"

#define __FILENUMBER 'PSID'


#ifdef ALLOC_PRAGMA

#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, NdisProtUnload)
#pragma alloc_text(PAGE, NdisProtOpen)
#pragma alloc_text(PAGE, NdisProtClose)

#endif // ALLOC_PRAGMA


//
// Globals:
//
NDISPROT_GLOBALS Globals = { 0 };

NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath)
/*++
Routine Description:
Called on loading. We create a device object to handle user-mode requests on, and register ourselves as a protocol with NDIS.
Arguments:
pDriverObject - Pointer to driver object created by system.
pRegistryPath - Pointer to the Unicode name of the registry path for this driver.
Return Value:
NT Status code
--*/
{
NDIS_PROTOCOL_CHARACTERISTICS protocolChar;
NTSTATUS status = STATUS_SUCCESS;
NDIS_STRING protoName = NDIS_STRING_CONST("NdisProt");
UNICODE_STRING ntDeviceName;
UNICODE_STRING win32DeviceName;
BOOLEAN fSymbolicLink = FALSE;
PDEVICE_OBJECT deviceObject = NULL;
UNREFERENCED_PARAMETER(pRegistryPath);
DEBUGP(DL_LOUD, ("DriverEntry\n"));
Globals.pDriverObject = pDriverObject;
NPROT_INIT_EVENT(&Globals.BindsComplete);
do {
// Create our device object using which an application can access NDIS devices.
RtlInitUnicodeString(&ntDeviceName, NT_DEVICE_NAME);
#ifndef WIN9X
status = IoCreateDeviceSecure(pDriverObject, 0, &ntDeviceName, FILE_DEVICE_NETWORK, FILE_DEVICE_SECURE_OPEN, FALSE, &SDDL_DEVOBJ_SYS_ALL_ADM_ALL, NULL, &deviceObject);
#else
status = IoCreateDevice(pDriverObject, 0, &ntDeviceName, FILE_DEVICE_NETWORK, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject);
#endif
if (!NT_SUCCESS(status))
// Either not enough memory to create a deviceobject or another deviceobject with the same name exits. This could happen if you install another instance of this device.
break;
RtlInitUnicodeString(&win32DeviceName, DOS_DEVICE_NAME);
status = IoCreateSymbolicLink(&win32DeviceName, &ntDeviceName);
if (!NT_SUCCESS(status))
break;
fSymbolicLink = TRUE;
deviceObject->Flags |= DO_DIRECT_IO;
Globals.ControlDeviceObject = deviceObject;
NPROT_INIT_LIST_HEAD(&Globals.OpenList);
NPROT_INIT_LOCK(&Globals.GlobalLock);
// Initialize the protocol characterstic structure
NdisZeroMemory(&protocolChar, sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
protocolChar.MajorNdisVersion = 5;
protocolChar.MinorNdisVersion = 0;
protocolChar.Name = protoName;
protocolChar.OpenAdapterCompleteHandler = NdisProtOpenAdapterComplete;
protocolChar.CloseAdapterCompleteHandler = NdisProtCloseAdapterComplete;
protocolChar.SendCompleteHandler = NdisProtSendComplete;
protocolChar.TransferDataCompleteHandler = NdisProtTransferDataComplete;
protocolChar.ResetCompleteHandler = NdisProtResetComplete;
protocolChar.RequestCompleteHandler = NdisProtRequestComplete;
protocolChar.ReceiveHandler = NdisProtReceive;
protocolChar.ReceiveCompleteHandler = NdisProtReceiveComplete;
protocolChar.StatusHandler = NdisProtStatus;
protocolChar.StatusCompleteHandler = NdisProtStatusComplete;
protocolChar.BindAdapterHandler = NdisProtBindAdapter;
protocolChar.UnbindAdapterHandler = NdisProtUnbindAdapter;
protocolChar.UnloadHandler = NULL;
protocolChar.ReceivePacketHandler = NdisProtReceivePacket;
protocolChar.PnPEventHandler = NdisProtPnPEventHandler;
// Register as a protocol driver
NdisRegisterProtocol((PNDIS_STATUS)&status, &Globals.NdisProtocolHandle, &protocolChar, sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
if (status != NDIS_STATUS_SUCCESS) {
DEBUGP(DL_WARN, ("Failed to register protocol with NDIS\n"));
status = STATUS_UNSUCCESSFUL;
break;
};
#ifdef NDIS51
Globals.PartialCancelId = NdisGeneratePartialCancelId();
Globals.PartialCancelId <<= ((sizeof(PVOID) - 1) * 8);
DEBUGP(DL_LOUD, ("DriverEntry: CancelId %lx\n", Globals.PartialCancelId));
#endif
// Now set only the dispatch points we would like to handle.
pDriverObject->MajorFunction[IRP_MJ_CREATE] = NdisProtOpen;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = NdisProtClose;
pDriverObject->MajorFunction[IRP_MJ_READ] = NdisProtRead;
pDriverObject->MajorFunction[IRP_MJ_WRITE] = NdisProtWrite;
pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = NdisProtCleanup;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = NdisProtIoControl;
pDriverObject->DriverUnload = NdisProtUnload;
status = STATUS_SUCCESS;
} while (FALSE);
if (!NT_SUCCESS(status)) {
if (deviceObject) {
IoDeleteDevice(deviceObject);
Globals.ControlDeviceObject = NULL;
};
if (fSymbolicLink)
IoDeleteSymbolicLink(&win32DeviceName);
};
return status;
};


VOID
NdisProtUnload(
IN PDRIVER_OBJECT DriverObject
)
/*++

Routine Description:

Free all the allocated resources, etc.

Arguments:

DriverObject - pointer to a driver object.

Return Value:

VOID.

--*/
{

UNICODE_STRING win32DeviceName;

UNREFERENCED_PARAMETER(DriverObject);

DEBUGP(DL_LOUD, ("Unload Enter\n"));

ndisprotUnregisterExCallBack();

//
// First delete the Control deviceobject and the corresponding
// symbolicLink
//
RtlInitUnicodeString(&win32DeviceName, DOS_DEVICE_NAME);

IoDeleteSymbolicLink(&win32DeviceName);

if (Globals.ControlDeviceObject) {
IoDeleteDevice(Globals.ControlDeviceObject);
Globals.ControlDeviceObject = NULL;
}

ndisprotDoProtocolUnload();

#if DBG
ndisprotAuditShutdown();
#endif

NPROT_FREE_LOCK(&Globals.GlobalLock);

NPROT_FREE_DBG_LOCK();

DEBUGP(DL_LOUD, ("Unload Exit\n"));
}



NTSTATUS
NdisProtOpen(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
/*++

Routine Description:

This is the dispatch routine for handling IRP_MJ_CREATE.
We simply succeed this.

Arguments:

pDeviceObject - Pointer to the device object.

pIrp - Pointer to the request packet.

Return Value:

Status is returned.

--*/
{
PIO_STACK_LOCATION pIrpSp;
NTSTATUS NtStatus = STATUS_SUCCESS;

UNREFERENCED_PARAMETER(pDeviceObject);

pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pIrpSp->FileObject->FsContext = NULL;

DEBUGP(DL_INFO, ("Open: FileObject %p\n", pIrpSp->FileObject));

pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = NtStatus;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);

return NtStatus;
}

NTSTATUS
NdisProtClose(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
/*++

Routine Description:

This is the dispatch routine for handling IRP_MJ_CLOSE.
We simply succeed this.

Arguments:

pDeviceObject - Pointer to the device object.

pIrp - Pointer to the request packet.

Return Value:

Status is returned.

--*/
{
NTSTATUS NtStatus;
PIO_STACK_LOCATION pIrpSp;
PNDISPROT_OPEN_CONTEXT pOpenContext;

UNREFERENCED_PARAMETER(pDeviceObject);

pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pOpenContext = pIrpSp->FileObject->FsContext;

DEBUGP(DL_INFO, ("Close: FileObject %p\n",
IoGetCurrentIrpStackLocation(pIrp)->FileObject));

if (pOpenContext != NULL) {
NPROT_STRUCT_ASSERT(pOpenContext, oc);

//
// Deref the endpoint
//
NPROT_DEREF_OPEN(pOpenContext); // Close
}

pIrpSp->FileObject->FsContext = NULL;
NtStatus = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = NtStatus;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);

return NtStatus;
}



NTSTATUS
NdisProtCleanup(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
/*++

Routine Description:

This is the dispatch routine for handling IRP_MJ_CLEANUP.

Arguments:

pDeviceObject - Pointer to the device object.

pIrp - Pointer to the request packet.

Return Value:

Status is returned.

--*/
{
PIO_STACK_LOCATION pIrpSp;
NTSTATUS NtStatus;
NDIS_STATUS NdisStatus;
PNDISPROT_OPEN_CONTEXT pOpenContext;
ULONG PacketFilter;
ULONG BytesProcessed;


UNREFERENCED_PARAMETER(pDeviceObject);

pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pOpenContext = pIrpSp->FileObject->FsContext;

DEBUGP(DL_VERY_LOUD, ("Cleanup: FileObject %p, Open %p\n",
pIrpSp->FileObject, pOpenContext));

if (pOpenContext != NULL) {
NPROT_STRUCT_ASSERT(pOpenContext, oc);

//
// Mark this endpoint.
//
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);

NPROT_SET_FLAGS(pOpenContext->Flags, NUIOO_OPEN_FLAGS, NUIOO_OPEN_IDLE);
pOpenContext->pFileObject = NULL;

NPROT_RELEASE_LOCK(&pOpenContext->Lock);

//
// Set the packet filter to 0, telling NDIS that we aren't
// interested in any more receives.
//
PacketFilter = 0;
NdisStatus = ndisprotValidateOpenAndDoRequest(
pOpenContext,
NdisRequestSetInformation,
OID_GEN_CURRENT_PACKET_FILTER,
&PacketFilter,
sizeof(PacketFilter),
&BytesProcessed,
FALSE // Don't wait for device to be powered on
);

if (NdisStatus != NDIS_STATUS_SUCCESS) {
DEBUGP(DL_INFO, ("Cleanup: Open %p, set packet filter (%x) failed: %x\n",
pOpenContext, PacketFilter, NdisStatus));
//
// Ignore the result. If this failed, we may continue
// to get indicated receives, which will be handled
// appropriately.
//
NdisStatus = NDIS_STATUS_SUCCESS;
}

//
// Cancel any pending reads.
//
ndisprotCancelPendingReads(pOpenContext);

//
// Cancel pending control irp for status indication.
//
ndisServiceIndicateStatusIrp(pOpenContext,
0,
NULL,
0,
TRUE);

//
// Clean up the receive packet queue
//
ndisprotFlushReceiveQueue(pOpenContext);
}

NtStatus = STATUS_SUCCESS;

pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = NtStatus;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);

DEBUGP(DL_INFO, ("Cleanup: OpenContext %p\n", pOpenContext));

return (NtStatus);
}

NTSTATUS NdisProtIoControl(IN PDEVICE_OBJECT pDeviceObject,IN PIRP pIrp)
/*++
Routine Description:
This is the dispatch routine for handling device ioctl requests.
Arguments:
pDeviceObject - Pointer to the device object.
pIrp - Pointer to the request packet.
Return Value:
Status is returned.
--*/
{
PIO_STACK_LOCATION pIrpSp;
ULONG FunctionCode;
NTSTATUS NtStatus;
NDIS_STATUS Status;
PNDISPROT_OPEN_CONTEXT pOpenContext;
ULONG BytesReturned;
USHORT EthType;
#if !DBG
UNREFERENCED_PARAMETER(pDeviceObject);
#endif
DEBUGP(DL_LOUD, ("IoControl: DevObj %p, Irp %p\n", pDeviceObject, pIrp));
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
FunctionCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode;
pOpenContext = (PNDISPROT_OPEN_CONTEXT)pIrpSp->FileObject->FsContext;
BytesReturned = 0;
switch (FunctionCode) {
case IOCTL_NDISPROT_BIND_WAIT:
{
// 所有的DeviceIoControl请求的都应该是用的缓冲方式。这里只是确认一下。
NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED);
// 非常简单。等待一个全局事件。这个全局变量会在绑定完成的时候被设置。如果等待到了或者超时了(5秒)则返回。
if (NPROT_WAIT_EVENT(&Globals.BindsComplete, 5000))
NtStatus = STATUS_SUCCESS;
else
NtStatus = STATUS_TIMEOUT;
DEBUGP(DL_INFO, ("IoControl: BindWait returning %x\n", NtStatus));
break;
};
case IOCTL_NDISPROT_QUERY_BINDING:
{
NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED);
Status = ndisprotQueryBinding(pIrp->AssociatedIrp.SystemBuffer, pIrpSp->Parameters.DeviceIoControl.InputBufferLength, pIrpSp->Parameters.DeviceIoControl.OutputBufferLength, &BytesReturned);
NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus);
DEBUGP(DL_LOUD, ("IoControl: QueryBinding returning %x\n", NtStatus));
break;
};
case IOCTL_NDISPROT_OPEN_DEVICE:
{
NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED);
if (pOpenContext != NULL) {
NPROT_STRUCT_ASSERT(pOpenContext, oc);
DEBUGP(DL_WARN, ("IoControl: OPEN_DEVICE: FileObj %p alreadyassociated with open %p\n", pIrpSp->FileObject, pOpenContext));
NtStatus = STATUS_DEVICE_BUSY;
break;
};
NtStatus = ndisprotOpenDevice(pIrp->AssociatedIrp.SystemBuffer, pIrpSp->Parameters.DeviceIoControl.InputBufferLength, pIrpSp->FileObject, &pOpenContext);
if (NT_SUCCESS(NtStatus))
DEBUGP(DL_VERY_LOUD, ("IoControl OPEN_DEVICE: Open %p <-> FileObject %p\n", pOpenContext, pIrpSp->FileObject));
break;
};
case IOCTL_NDISPROT_QUERY_OID_VALUE:
{
NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED);
if (pOpenContext != NULL) {
Status = ndisprotQueryOidValue(pOpenContext, pIrp->AssociatedIrp.SystemBuffer, pIrpSp->Parameters.DeviceIoControl.OutputBufferLength, &BytesReturned);
NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus);
}
else
NtStatus = STATUS_DEVICE_NOT_CONNECTED;
break;
};
case IOCTL_NDISPROT_SET_OID_VALUE:
{
NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED);
if (pOpenContext != NULL) {
Status = ndisprotSetOidValue(pOpenContext, pIrp->AssociatedIrp.SystemBuffer, pIrpSp->Parameters.DeviceIoControl.InputBufferLength);
BytesReturned = 0;
NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus);
}
else
NtStatus = STATUS_DEVICE_NOT_CONNECTED;
break;
};
case IOCTL_NDISPROT_INDICATE_STATUS:
{
NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED);
if (pOpenContext != NULL)
NtStatus = ndisprotQueueStatusIndicationIrp(pOpenContext, pIrp, &BytesReturned);
else
NtStatus = STATUS_DEVICE_NOT_CONNECTED;
break;
};
default:
{
NtStatus = STATUS_NOT_SUPPORTED;
break;
};
};
if (NtStatus != STATUS_PENDING) {
pIrp->IoStatus.Information = BytesReturned;
pIrp->IoStatus.Status = NtStatus;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
};
return NtStatus;
};


NTSTATUS ndisprotOpenDevice(
__in_bcount(DeviceNameLength) IN PUCHAR pDeviceName,// pDeviceName 设备对象名
IN ULONG DeviceNameLength,// DeviceNameLength 设备对象的长度
IN PFILE_OBJECT pFileObject,// pFileObject 文件对象指针
OUT PNDISPROT_OPEN_CONTEXT* ppOpenContext
) {
PNDISPROT_OPEN_CONTEXT pOpenContext;
NTSTATUS NtStatus;
ULONG PacketFilter;
NDIS_STATUS NdisStatus;
ULONG BytesProcessed;
PNDISPROT_OPEN_CONTEXT pCurrentOpenContext = NULL;
pOpenContext = NULL;
do {
// 根据设备名找到打开上下文。请注意这个中间会调用增加打开上下文引用计数,所以后面要解引用。
pOpenContext = ndisprotLookupDevice(pDeviceName, DeviceNameLength);
// 如果找不到打开上下文,说明没绑定过...
if (pOpenContext == NULL) {
DEBUGP(DL_WARN, ("ndisprotOpenDevice: couldn't find device\n"));
NtStatus = STATUS_OBJECT_NAME_NOT_FOUND;
break;
};
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
// 如果找到了,但是不是打开空闲状态,则返回设备忙。
if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_OPEN_FLAGS, NUIOO_OPEN_IDLE)) {
NPROT_ASSERT(pOpenContext->pFileObject != NULL);
DEBUGP(DL_WARN, ("ndisprotOpenDevice: Open %p/%x already associated with another FileObject %p\n", pOpenContext, pOpenContext->Flags, pOpenContext->pFileObject));
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
// 注意解引用。
NPROT_DEREF_OPEN(pOpenContext); // ndisprotOpenDevice failure
NtStatus = STATUS_DEVICE_BUSY;
break;
};
// 比较交换。首先比较pFileObject->FsContext和NULL.如果是NULL,则用pFileObject->FsContext设置为pOpenContext,然后返回NULL。如果不是NULL,则不交换,并返回pFileObject->FsContext
if ((pCurrentOpenContext = InterlockedCompareExchangePointer(&(pFileObject->FsContext), pOpenContext, NULL)) != NULL) {
// 到了这里,说明另一个打开已经使用了这个文件对象。这个设备不支持两次打开。到这里直接返回失败即可。
DEBUGP(DL_WARN, ("ndisprotOpenDevice: FileObject %p already associated with another Open %p/%x\n", pFileObject, pCurrentOpenContext, pCurrentOpenContext->Flags)); //BUG
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
NPROT_DEREF_OPEN(pOpenContext); // ndisprotOpenDevice failure
NtStatus = STATUS_INVALID_DEVICE_REQUEST;
break;
};
// 这个打开上下文被打开了,保存在这个文件对象的FsContext中。这里也保存
pOpenContext->pFileObject = pFileObject;
NPROT_SET_FLAGS(pOpenContext->Flags, NUIOO_OPEN_FLAGS, NUIOO_OPEN_ACTIVE);
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
// 设置PacketFilter,使之能收到包。
PacketFilter = NUIOO_PACKET_FILTER;
NdisStatus = ndisprotValidateOpenAndDoRequest(pOpenContext, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &PacketFilter, sizeof(PacketFilter), &BytesProcessed, TRUE); // TRUE:Do wait for power on
// 不成功的话
if (NdisStatus != NDIS_STATUS_SUCCESS) {
DEBUGP(DL_WARN, ("openDevice: Open %p: set packet filter (%x) failed: %x\n", pOpenContext, PacketFilter, NdisStatus));
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
// 不成功的话,去掉FileObject->FsContext设置,如果pFileObject->FsContext是pOpenContext,则去掉。
pCurrentOpenContext = InterlockedCompareExchangePointer(&(pFileObject->FsContext), NULL, pOpenContext);
NPROT_ASSERT(pCurrentOpenContext == pOpenContext);
NPROT_SET_FLAGS(pOpenContext->Flags, NUIOO_OPEN_FLAGS, NUIOO_OPEN_IDLE);
pOpenContext->pFileObject = NULL;
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
NPROT_DEREF_OPEN(pOpenContext); // ndisprotOpenDevice failure
NDIS_STATUS_TO_NT_STATUS(NdisStatus, &NtStatus);
break;
};
// 返回打开上下文。
*ppOpenContext = pOpenContext;
NtStatus = STATUS_SUCCESS;
} while (FALSE);
return (NtStatus);
};


VOID
ndisprotRefOpen(
IN PNDISPROT_OPEN_CONTEXT pOpenContext
)
/*++

Routine Description:

Reference the given open context.

NOTE: Can be called with or without holding the opencontext lock.

Arguments:

pOpenContext - pointer to open context

Return Value:

None

--*/
{
NdisInterlockedIncrement((PLONG)&pOpenContext->RefCount);
}


VOID
ndisprotDerefOpen(
IN PNDISPROT_OPEN_CONTEXT pOpenContext
)
/*++

Routine Description:

Dereference the given open context. If the ref count goes to zero,
free it.

NOTE: called without holding the opencontext lock

Arguments:

pOpenContext - pointer to open context

Return Value:

None

--*/
{
if (NdisInterlockedDecrement((PLONG)&pOpenContext->RefCount) == 0) {
DEBUGP(DL_INFO, ("DerefOpen: Open %p, Flags %x, ref count is zero!\n",
pOpenContext, pOpenContext->Flags));

NPROT_ASSERT(pOpenContext->BindingHandle == NULL);
NPROT_ASSERT(pOpenContext->RefCount == 0);
NPROT_ASSERT(pOpenContext->pFileObject == NULL);

pOpenContext->oc_sig++;

//
// Free it.
//
NPROT_FREE_LOCK(&pOpenContext->Lock);

NPROT_FREE_MEM(pOpenContext);
}
}


#if DBG
VOID
ndisprotDbgRefOpen(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN ULONG FileNumber,
IN ULONG LineNumber
) {
DEBUGP(DL_VERY_LOUD, (" RefOpen: Open %p, old ref %d, File %c%c%c%c, line %d\n",
pOpenContext,
pOpenContext->RefCount,
(CHAR)(FileNumber),
(CHAR)(FileNumber >> 8),
(CHAR)(FileNumber >> 16),
(CHAR)(FileNumber >> 24),
LineNumber));

ndisprotRefOpen(pOpenContext);
}

VOID
ndisprotDbgDerefOpen(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN ULONG FileNumber,
IN ULONG LineNumber
) {
DEBUGP(DL_VERY_LOUD, ("DerefOpen: Open %p, old ref %d, File %c%c%c%c, line %d\n",
pOpenContext,
pOpenContext->RefCount,
(CHAR)(FileNumber),
(CHAR)(FileNumber >> 8),
(CHAR)(FileNumber >> 16),
(CHAR)(FileNumber >> 24),
LineNumber));

ndisprotDerefOpen(pOpenContext);
}

#endif // DBG

nuiouser.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/*++

Copyright (c) 2000 Microsoft Corporation

Module Name:

nuiouser.h

Abstract:

Constants and types to access the NDISPROT driver.
Users must also include ntddndis.h

Environment:

User/Kernel mode.

Revision History:

--*/

#ifndef __NUIOUSER__H
#define __NUIOUSER__H


#define FSCTL_NDISPROT_BASE FILE_DEVICE_NETWORK

#define _NDISPROT_CTL_CODE(_Function, _Method, _Access) \
CTL_CODE(FSCTL_NDISPROT_BASE, _Function, _Method, _Access)

#define IOCTL_NDISPROT_OPEN_DEVICE \
_NDISPROT_CTL_CODE(0x200, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

#define IOCTL_NDISPROT_QUERY_OID_VALUE \
_NDISPROT_CTL_CODE(0x201, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

#define IOCTL_NDISPROT_SET_OID_VALUE \
_NDISPROT_CTL_CODE(0x205, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

#define IOCTL_NDISPROT_QUERY_BINDING \
_NDISPROT_CTL_CODE(0x203, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

#define IOCTL_NDISPROT_BIND_WAIT \
_NDISPROT_CTL_CODE(0x204, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

#define IOCTL_NDISPROT_INDICATE_STATUS \
_NDISPROT_CTL_CODE(0x206, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)



//
// Structure to go with IOCTL_NDISPROT_QUERY_OID_VALUE.
// The Data part is of variable length, determined by
// the input buffer length passed to DeviceIoControl.
//
typedef struct _NDISPROT_QUERY_OID {
NDIS_OID Oid;
UCHAR Data[sizeof(ULONG)];

} NDISPROT_QUERY_OID, * PNDISPROT_QUERY_OID;

//
// Structure to go with IOCTL_NDISPROT_SET_OID_VALUE.
// The Data part is of variable length, determined
// by the input buffer length passed to DeviceIoControl.
//
typedef struct _NDISPROT_SET_OID {
NDIS_OID Oid;
UCHAR Data[sizeof(ULONG)];

} NDISPROT_SET_OID, * PNDISPROT_SET_OID;


//
// Structure to go with IOCTL_NDISPROT_QUERY_BINDING.
// The input parameter is BindingIndex, which is the
// index into the list of bindings active at the driver.
// On successful completion, we get back a device name
// and a device descriptor (friendly name).
//
typedef struct _NDISPROT_QUERY_BINDING {
ULONG BindingIndex; // 0-based binding number
ULONG DeviceNameOffset; // from start of this struct
ULONG DeviceNameLength; // in bytes
ULONG DeviceDescrOffset; // from start of this struct
ULONG DeviceDescrLength; // in bytes

} NDISPROT_QUERY_BINDING, * PNDISPROT_QUERY_BINDING;

//
// Structure to go with IOCTL_NDISPROT_INDICATE_STATUS.
// NDISPROT copies the status indicated by the NIC and
// also the data indicated in the StatusBuffer.
//
typedef struct _NDISPROT_INDICATE_STATUS {
ULONG IndicatedStatus; // NDIS_STATUS
ULONG StatusBufferOffset; // from start of this struct
ULONG StatusBufferLength; // in bytes
} NDISPROT_INDICATE_STATUS, * PNDISPROT_INDICATE_STATUS;

#endif // __NUIOUSER__H

precomp.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#pragma warning(disable:4214)   // bit field types other than int

#pragma warning(disable:4201) // nameless struct/union
#pragma warning(disable:4115) // named type definition in parentheses
#pragma warning(disable:4127) // conditional expression is constant
#pragma warning(disable:4054) // cast of function pointer to PVOID
#pragma warning(disable:4244) // conversion from 'int' to 'BOOLEAN', possible loss of data
#pragma warning(disable:4206) // nonstandard extension used : translation unit is empty

#include "ndis.h"
#include "ntddk.h"
#include "debug.h"
#include "ndisprot.h"
#include "macros.h"
#include "nuiouser.h"
#include <wdmsec.h>

recv.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
/*++

Copyright (c) 2000 Microsoft Corporation

Module Name:

recv.c

Abstract:

NDIS protocol entry points and utility routines to handle receiving
data.

Environment:

Kernel mode only.

Revision History:

--*/

#include "precomp.h"

#define __FILENUMBER 'VCER'



// 分发函数之一,处理读请求。
NTSTATUS NdisProtRead(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp) {
PIO_STACK_LOCATION pIrpSp;
NTSTATUS NtStatus;
PNDISPROT_OPEN_CONTEXT pOpenContext;
UNREFERENCED_PARAMETER(pDeviceObject);
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pOpenContext = pIrpSp->FileObject->FsContext;
do {
// 检测打开上下文的可靠性
if (pOpenContext == NULL) {
DEBUGP(DL_FATAL, ("Read: NULL FsContext on FileObject %p\n", pIrpSp->FileObject));
NtStatus = STATUS_INVALID_HANDLE;
break;
};
NPROT_STRUCT_ASSERT(pOpenContext, oc);
// Read和Write都是使用的直接IO操作,所以应该使用MdlAddress来传递缓冲。如果不是,返回非法参数错误。
if (pIrp->MdlAddress == NULL) {
DEBUGP(DL_FATAL, ("Read: NULL MDL address on IRP %p\n", pIrp));
NtStatus = STATUS_INVALID_PARAMETER;
break;
};
// 得到缓冲的虚拟地址。
if (MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority) == NULL) {
DEBUGP(DL_FATAL, ("Read: MmGetSystemAddr failed for IRP %p, MDL %p\n", pIrp, pIrp->MdlAddress));
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
};
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_ACTIVE)) {
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
NtStatus = STATUS_INVALID_HANDLE;
break;
};
// 将这个请求插入处理队列里。并把打开上下文引用计数增加就1.把未处理读请求数目增加1.
NPROT_INSERT_TAIL_LIST(&pOpenContext->PendedReads, &pIrp->Tail.Overlay.ListEntry);
NPROT_REF_OPEN(pOpenContext); // pended read IRP
pOpenContext->PendedReadCount++;
// 标记IRP未决。给IRP设置一个取消函数,使之变得可取消。
pIrp->Tail.Overlay.DriverContext[0] = (PVOID)pOpenContext;
IoMarkIrpPending(pIrp);
IoSetCancelRoutine(pIrp, NdisProtCancelRead);
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
NtStatus = STATUS_PENDING;
// 调用一个处理例程来处理所有未决的读请求。
ndisprotServiceReads(pOpenContext);
} while (FALSE);
// 读请求只返回STATUS_PENDING.如果不是,则说明出错,按错误返回。
if (NtStatus != STATUS_PENDING) {
NPROT_ASSERT(NtStatus != STATUS_SUCCESS);
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = NtStatus;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
};
return (NtStatus);
};


VOID
NdisProtCancelRead(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
/*++

Routine Description:

Cancel a pending read IRP. We unlink the IRP from the open context
queue and complete it.

Arguments:

pDeviceObject - pointer to our device object
pIrp - IRP to be cancelled

Return Value:

None

--*/
{
PNDISPROT_OPEN_CONTEXT pOpenContext;
PLIST_ENTRY pIrpEntry;
BOOLEAN Found;

UNREFERENCED_PARAMETER(pDeviceObject);

IoReleaseCancelSpinLock(pIrp->CancelIrql);

Found = FALSE;

pOpenContext = (PNDISPROT_OPEN_CONTEXT)pIrp->Tail.Overlay.DriverContext[0];
NPROT_STRUCT_ASSERT(pOpenContext, oc);

NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);

//
// Locate the IRP in the pended read queue and remove it if found.
//
for (pIrpEntry = pOpenContext->PendedReads.Flink;
pIrpEntry != &pOpenContext->PendedReads;
pIrpEntry = pIrpEntry->Flink) {
if (pIrp == CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry)) {
NPROT_REMOVE_ENTRY_LIST(&pIrp->Tail.Overlay.ListEntry);
pOpenContext->PendedReadCount--;
Found = TRUE;
break;
}
}

NPROT_RELEASE_LOCK(&pOpenContext->Lock);

if (Found) {
DEBUGP(DL_INFO, ("CancelRead: Open %p, IRP %p\n", pOpenContext, pIrp));
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);

NPROT_DEREF_OPEN(pOpenContext); // Cancel removed pended Read
}
}



VOID ndisprotServiceReads(IN PNDISPROT_OPEN_CONTEXT pOpenContext)
/*++
Routine Description:
Utility routine to copy received data into user buffers and complete READ IRPs.
Arguments:
pOpenContext - pointer to open context
Return Value:
None
--*/
{
PIRP pIrp = NULL;
PLIST_ENTRY pIrpEntry;
PNDIS_PACKET pRcvPacket;
PLIST_ENTRY pRcvPacketEntry;
PUCHAR pSrc, pDst;
ULONG BytesRemaining; // at pDst
PNDIS_BUFFER pNdisBuffer;
ULONG BytesAvailable;
BOOLEAN FoundPendingIrp;
DEBUGP(DL_VERY_LOUD, ("ServiceReads: open %p/%x\n", pOpenContext, pOpenContext->Flags));
NPROT_REF_OPEN(pOpenContext); // temp ref - service reads
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
// 只要读请求队列和接收包队列同时不为空,则可以做...
while (!NPROT_IS_LIST_EMPTY(&pOpenContext->PendedReads) && !NPROT_IS_LIST_EMPTY(&pOpenContext->RecvPktQueue)) {
FoundPendingIrp = FALSE;
// 获得第一个未决读请求
pIrpEntry = pOpenContext->PendedReads.Flink;
while (pIrpEntry != &pOpenContext->PendedReads) {
// 从链表节点得到IRP
pIrp = CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry);
// 检查这个请求是否正在被取消。
if (IoSetCancelRoutine(pIrp, NULL)) {
// 把这个IRP出列。
NPROT_REMOVE_ENTRY_LIST(pIrpEntry);
FoundPendingIrp = TRUE;
break;
}
else {
// 如果是在去掉,则跳过这个IRP即可。使用下一个。
DEBUGP(DL_INFO, ("ServiceReads: open %p, skipping cancelled IRP %p\n", pOpenContext, pIrp));
pIrpEntry = pIrpEntry->Flink;
};
};
// 如果没有IRP,直接跳出结束。
if (FoundPendingIrp == FALSE)
break;
// 得到第一个包(最旧的),出队列。
pRcvPacketEntry = pOpenContext->RecvPktQueue.Flink;
NPROT_REMOVE_ENTRY_LIST(pRcvPacketEntry);
pOpenContext->RecvPktCount--;
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
NPROT_DEREF_OPEN(pOpenContext);
// 从节点获得包。
pRcvPacket = NPROT_LIST_ENTRY_TO_RCV_PKT(pRcvPacketEntry);
// Copy as much data as possible from the receive packet to the IRP MDL.
// 得到IRP的输出缓冲地址。然后尽量拷贝更多的数据。
pDst = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);
NPROT_ASSERT(pDst != NULL); // since it was already mapped
BytesRemaining = MmGetMdlByteCount(pIrp->MdlAddress);
pNdisBuffer = NDIS_PACKET_FIRST_NDIS_BUFFER(pRcvPacket);
// 请注意,每个PNDIS_BUFFER都是一个PMDL,同时PNDIS_BUFFER本身都是链表。用NdisGetNextBuffer可以从一个得到它的下面一个。包的数据实际上是保存在一个缓冲描述符链表里的。
while (BytesRemaining && (pNdisBuffer != NULL)) {
#ifndef WIN9X
NdisQueryBufferSafe(pNdisBuffer, &pSrc, &BytesAvailable, NormalPagePriority);
if (pSrc == NULL) {
DEBUGP(DL_FATAL, ("ServiceReads: Open %p, QueryBuffer failed for buffer %p\n", pOpenContext, pNdisBuffer));
break;
};
#else
NdisQueryBuffer(pNdisBuffer, &pSrc, &BytesAvailable);
#endif
// 如果还可以继续拷贝,就继续拷贝。
if (BytesAvailable) {
ULONG BytesToCopy = MIN(BytesAvailable, BytesRemaining);
NPROT_COPY_MEM(pDst, pSrc, BytesToCopy);
BytesRemaining -= BytesToCopy;
pDst += BytesToCopy;
};
NdisGetNextBuffer(pNdisBuffer, &pNdisBuffer);
};
// 拷贝好数据之后,结束IRP即可。
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = MmGetMdlByteCount(pIrp->MdlAddress) - BytesRemaining;
DEBUGP(DL_INFO, ("ServiceReads: Open %p, IRP %p completed with %d bytes\n", pOpenContext, pIrp, pIrp->IoStatus.Information));
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
// 如果这个包描述符不是从接收包池里分配的,那么就是从网卡驱动里重用的。如果是重用的,调用NdisReturnPackets归还给网卡驱动,让它释放。
if (NdisGetPoolFromPacket(pRcvPacket) != pOpenContext->RecvPacketPool)
NdisReturnPackets(&pRcvPacket, 1);
else
// 否则的话自己释放。
ndisprotFreeReceivePacket(pOpenContext, pRcvPacket);
NPROT_DEREF_OPEN(pOpenContext); // took out pended Read
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
pOpenContext->PendedReadCount--;
};
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
NPROT_DEREF_OPEN(pOpenContext); // temp ref - service reads
return;
};




NDIS_STATUS NdisProtReceive(IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_HANDLE MacReceiveContext, __in_bcount(HeaderBufferSize) IN PVOID pHeaderBuffer, IN UINT HeaderBufferSize, __in_bcount(LookaheadBufferSize) IN PVOID pLookaheadBuffer, IN UINT LookaheadBufferSize, IN UINT PacketSize)
/*++
Routine Description:
Our protocol receive handler called by NDIS, typically if we have a miniport below that doesn't indicate packets.
We make a local packet/buffer copy of this data, queue it up, and kick off the read service routine.
Arguments:
ProtocolBindingContext - pointer to open context
MacReceiveContext - for use in NdisTransferData
pHeaderBuffer - pointer to data header
HeaderBufferSize - size of the above
pLookaheadBuffer - pointer to buffer containing lookahead data
LookaheadBufferSize - size of the above
PacketSize - size of the entire packet, minus header size.
Return Value:
NDIS_STATUS_NOT_ACCEPTED - if this packet is uninteresting
NDIS_STATUS_SUCCESS - if we processed this successfully
--*/
{
PNDISPROT_OPEN_CONTEXT pOpenContext;
NDIS_STATUS Status;
PNDIS_PACKET pRcvPacket;
PUCHAR pRcvData;
UINT BytesTransferred;
PNDIS_BUFFER pOriginalNdisBuffer, pPartialNdisBuffer;
// 获得绑定句柄。
pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext;
NPROT_STRUCT_ASSERT(pOpenContext, oc);
pRcvPacket = NULL;
pRcvData = NULL;
Status = NDIS_STATUS_SUCCESS;
do {
// 如果头长度不是以太网包头的长度,则不接收这个包。本协议只接收以太网包。
if (HeaderBufferSize != sizeof(NDISPROT_ETH_HEADER)) {
Status = NDIS_STATUS_NOT_ACCEPTED;
break;
};
// 这个比较比较奇怪。难道头长度是负数?
if ((PacketSize + HeaderBufferSize) < PacketSize) {
Status = NDIS_STATUS_NOT_ACCEPTED;
break;
};
// 分配一个包。包括包描述符和缓冲描述符,以及内存空间,一次性分配好。
pRcvPacket = ndisprotAllocateReceivePacket(pOpenContext, PacketSize + HeaderBufferSize, &pRcvData);
// 如果分配失败了,就不再接收这个包了。
if ((pRcvPacket == NULL) || (pRcvData == NULL)) {
Status = NDIS_STATUS_NOT_ACCEPTED;
break;
};
// 内存拷贝。先拷贝以太网包头。
NdisMoveMappedMemory(pRcvData, pHeaderBuffer, HeaderBufferSize);
// 检查前视区里是否包含了完整包的数据。
if (PacketSize == LookaheadBufferSize) {
// 如果前视区已经包括了整个数据包,那么调用NdisCopyLookaheadData就得到了完整的包,然后调用ndisprotQueueReceivePacket将这个包插入队列即可。
NdisCopyLookaheadData(pRcvData + HeaderBufferSize, pLookaheadBuffer, LookaheadBufferSize, pOpenContext->MacOptions);
ndisprotQueueReceivePacket(pOpenContext, pRcvPacket);
}
else {
// 否则的话,需要分配一个新的缓冲描述符。请注意这个描述符号对应的是从包缓冲区开始之后HeaderBufferSize个字节之后处开始的空间(pRcvData + HeaderBufferSize)。
NdisAllocateBuffer(&Status, &pPartialNdisBuffer, pOpenContext->RecvBufferPool, pRcvData + HeaderBufferSize, PacketSize);
if (Status == NDIS_STATUS_SUCCESS) {
// 如果成功了,就把包上原有的缓冲解链。使原来的缓冲描述符脱离包描述符。
NdisUnchainBufferAtFront(pRcvPacket, &pOriginalNdisBuffer);
// 现在把原来的包描述符保存在包描述符中(保留以备后用)
NPROT_RCV_PKT_TO_ORIGINAL_BUFFER(pRcvPacket) = pOriginalNdisBuffer;
// 然后把新的缓冲描述符连接到包上。
NdisChainBufferAtBack(pRcvPacket, pPartialNdisBuffer);
DEBUGP(DL_LOUD, ("Receive: setting up for TransferData: Pkt %p, OriginalBuf %p, PartialBuf %p\n", pRcvPacket, pOriginalNdisBuffer, pPartialNdisBuffer));
// 然后调用NdisTransferData来传输数据包剩余的部分。这个调用完成之后,协议特征中的NdisProtTransferDataComplete会被调用。
NdisTransferData(&Status, pOpenContext->BindingHandle, MacReceiveContext, 0, PacketSize, pRcvPacket, &BytesTransferred);
}
else
// 如果失败了,就不会调用NdisTransferData。但是我们还是要在NdisProtTransferDataComplete中做最后的处理。所以自己填写BytesTransferred。
BytesTransferred = 0;
if (Status != NDIS_STATUS_PENDING)
// 如果前面就失败了,我们自己调用NdisProtTransferDataComplete。
NdisProtTransferDataComplete((NDIS_HANDLE)pOpenContext, pRcvPacket, Status, BytesTransferred);
};
} while (FALSE);
DEBUGP(DL_LOUD, ("Receive: Open %p, Pkt %p, Size %d\n", pOpenContext, pRcvPacket, PacketSize));
return Status;
};



VOID NdisProtTransferDataComplete(IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET pNdisPacket, IN NDIS_STATUS TransferStatus, IN UINT BytesTransferred)
/*++
Routine Description:
NDIS entry point called to signal completion of a call to NdisTransferData that had pended.
Arguments:
ProtocolBindingContext - pointer to open context
pNdisPacket - our receive packet into which data is transferred
TransferStatus - status of the transfer
BytesTransferred - bytes copied into the packet.
Return Value:
None
--*/
{
PNDISPROT_OPEN_CONTEXT pOpenContext;
PNDIS_BUFFER pOriginalBuffer, pPartialBuffer;
UNREFERENCED_PARAMETER(BytesTransferred);
pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext;
NPROT_STRUCT_ASSERT(pOpenContext, oc);
// 得到保存过的旧的缓冲描述符。要记得在传输之前,为了让传输的内容正确的写到以太网包头后,我们分配了一个新的缓冲描述符替换了旧的缓冲描述符。现在要恢复它了。
pOriginalBuffer = NPROT_RCV_PKT_TO_ORIGINAL_BUFFER(pNdisPacket);
if (pOriginalBuffer != NULL) {
// 和前面的替换时的操作一样,先Unchain,然后再调用Chain。调用之后已经恢复了使用旧的包描述符。
NdisUnchainBufferAtFront(pNdisPacket, &pPartialBuffer);
NdisChainBufferAtBack(pNdisPacket, pOriginalBuffer);
DEBUGP(DL_LOUD, ("TransferComp: Pkt %p, OrigBuf %p, PartialBuf %p\n", pNdisPacket, pOriginalBuffer, pPartialBuffer));
ASSERT(pPartialBuffer != NULL);
// 那么那个新的包描述符已经没用了,调用NdisFreeBuffer释放它。
if (pPartialBuffer != NULL)
NdisFreeBuffer(pPartialBuffer);
};
if (TransferStatus == NDIS_STATUS_SUCCESS)
// 如果传输是成功的,将包保存到接收队列中。
ndisprotQueueReceivePacket(pOpenContext, pNdisPacket);
else
// 如果传输失败了,直接释放这个包。
ndisprotFreeReceivePacket(pOpenContext, pNdisPacket);
return;
};


VOID
NdisProtReceiveComplete(
IN NDIS_HANDLE ProtocolBindingContext
)
/*++

Routine Description:

Protocol entry point called by NDIS when the miniport
has finished indicating up a batch of receives.

We ignore this.

Arguments:

ProtocolBindingContext - pointer to open context

Return Value:

None

--*/
{
PNDISPROT_OPEN_CONTEXT pOpenContext;

pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext;
NPROT_STRUCT_ASSERT(pOpenContext, oc);

return;
}


INT NdisProtReceivePacket(IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET pNdisPacket)
/*++
Routine Description:
Protocol entry point called by NDIS if the driver below uses NDIS 4 style receive packet indications.
If the miniport allows us to hold on to this packet, we use it as is, otherwise we make a copy.
Arguments:
ProtocolBindingContext - pointer to open context
pNdisPacket - the packet being indicated up.
Return Value:
None
--*/
{
PNDISPROT_OPEN_CONTEXT pOpenContext;
PNDIS_BUFFER pNdisBuffer;
UINT BufferLength;
PNDISPROT_ETH_HEADER pEthHeader;
PNDIS_PACKET pCopyPacket;
PUCHAR pCopyBuf;
UINT TotalPacketLength;
UINT BytesCopied;
INT RefCount = 0;
NDIS_STATUS Status;
pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext;
NPROT_STRUCT_ASSERT(pOpenContext, oc);
#ifdef NDIS51
NdisGetFirstBufferFromPacketSafe(pNdisPacket, &pNdisBuffer, &pEthHeader, &BufferLength, &TotalPacketLength, NormalPagePriority);
if (pEthHeader == NULL)
// The system is low on resources. Set up to handle failure below.
BufferLength = 0;
#else
// 从包描述符中得到第一个缓冲描述符。
NdisGetFirstBufferFromPacket(pNdisPacket, &pNdisBuffer, &pEthHeader, &BufferLength, &TotalPacketLength);
#endif
do {
// 如果这个包的长度比以太网包头还要小,丢弃之。
if (BufferLength < sizeof(NDISPROT_ETH_HEADER)) {
DEBUGP(DL_WARN, ("ReceivePacket: Open %p, runt pkt %p, first buffer length %d\n", pOpenContext, pNdisPacket, BufferLength));
Status = NDIS_STATUS_NOT_ACCEPTED;
break;
};
DEBUGP(DL_LOUD, ("ReceivePacket: Open %p, interesting pkt %p\n", pOpenContext, pNdisPacket));
// 如果这个包有NDIS_STATUS_RESOURCES状态,则必须拷贝而不能重用该包。当然这样就比较消耗时间和资源了。
if ((NDIS_GET_PACKET_STATUS(pNdisPacket) == NDIS_STATUS_RESOURCES) || pOpenContext->bRunningOnWin9x) {
// 下面是分配一个包并拷贝其内容。读者可以参考前面讲过的内容来理解。
pCopyPacket = ndisprotAllocateReceivePacket(pOpenContext, TotalPacketLength, &pCopyBuf);
if (pCopyPacket == NULL) {
DEBUGP(DL_FATAL, ("ReceivePacket: Open %p, failed to alloc copy, %d bytes\n", pOpenContext, TotalPacketLength));
break;
};
// 调用NdisCopyFromPacketToPacket来拷贝包。当然在拷贝之前调用者必须确保目标包的缓冲区长度是足够的。
NdisCopyFromPacketToPacket(pCopyPacket, 0, TotalPacketLength, pNdisPacket, 0, &BytesCopied);
NPROT_ASSERT(BytesCopied == TotalPacketLength);
// 那么现在开始就用新的包了。
pNdisPacket = pCopyPacket;
}
else
// 返回值。返回值表示的是我们已经一次引用了这个包。当处理完结的时候,我们就可以调用NdisReturnPackets来要求下层驱动释放这个包了。本函数把RefCount当做返回值。如果返回了0,那么下层驱动会认为我们不再需要这个数据包。
RefCount = 1;
// 将数据包放入队列里。
ndisprotQueueReceivePacket(pOpenContext, pNdisPacket);
} while (FALSE);
return (RefCount);
};


VOID ndisprotQueueReceivePacket(IN PNDISPROT_OPEN_CONTEXT pOpenContext, IN PNDIS_PACKET pRcvPacket)
/*++
Routine Description:
Queue up a received packet on the open context structure. If the queue size goes beyond a water mark, discard a packet at the head of the queue.
Finally, run the queue service routine.
Arguments:
pOpenContext - pointer to open context
pRcvPacket - the received packet
Return Value:
None
--*/
{
PLIST_ENTRY pEnt;
PLIST_ENTRY pDiscardEnt;
PNDIS_PACKET pDiscardPkt;
do {
pEnt = NPROT_RCV_PKT_TO_LIST_ENTRY(pRcvPacket);
NPROT_REF_OPEN(pOpenContext); // queued rcv packet
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
// 如果处于活动的状态,并且正确的电源状态,那么就把这个包插入接收缓冲链表中。
if (NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_ACTIVE) &&
(pOpenContext->PowerState == NetDeviceStateD0)) {
NPROT_INSERT_TAIL_LIST(&pOpenContext->RecvPktQueue, pEnt);
pOpenContext->RecvPktCount++;
DEBUGP(DL_VERY_LOUD, ("QueueReceivePacket: open %p, queued pkt %p, queue size %d\n", pOpenContext, pRcvPacket, pOpenContext->RecvPktCount));
}
else {
// 否则的话,就直接释放掉这个包即可。
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
ndisprotFreeReceivePacket(pOpenContext, pRcvPacket);
NPROT_DEREF_OPEN(pOpenContext); // dropped rcv packet - bad state
break;
};
// 如果输入缓冲区里包太多了,就要删除一个。
if (pOpenContext->RecvPktCount > MAX_RECV_QUEUE_SIZE) {
// 要删除的包的链节点指针
pDiscardEnt = pOpenContext->RecvPktQueue.Flink;
NPROT_REMOVE_ENTRY_LIST(pDiscardEnt);
// 接收包数量减去1
pOpenContext->RecvPktCount--;
// 可以释放锁了。
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
// 从链节点转换为包的指针
pDiscardPkt = NPROT_LIST_ENTRY_TO_RCV_PKT(pDiscardEnt);
// 把包释放掉。
ndisprotFreeReceivePacket(pOpenContext, pDiscardPkt);
// 打开上下文解引用。这是因为每入队一个都要增加一次引用计数。
NPROT_DEREF_OPEN(pOpenContext); // dropped rcv packet - queue too long
DEBUGP(DL_INFO, ("QueueReceivePacket: open %p queue too long, discarded pkt %p\n", pOpenContext, pDiscardPkt));
}
else
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
// 服务函数。这个函数看是否有未决的读请求。如果有,就取包来完成这个请求。
ndisprotServiceReads(pOpenContext);
} while (FALSE);
return;
};


PNDIS_PACKET
ndisprotAllocateReceivePacket(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN UINT DataLength,
OUT PUCHAR* ppDataBuffer
)
/*++

Routine Description:

Allocate resources to copy and queue a received packet.

Arguments:

pOpenContext - pointer to open context for received packet
DataLength - total length in bytes of the packet
ppDataBuffer - place to return pointer to allocated buffer

Return Value:

Pointer to NDIS packet if successful, else NULL.

--*/
{
PNDIS_PACKET pNdisPacket;
PNDIS_BUFFER pNdisBuffer;
PUCHAR pDataBuffer;
NDIS_STATUS Status;

pNdisPacket = NULL;
pNdisBuffer = NULL;
pDataBuffer = NULL;

do {
NPROT_ALLOC_MEM(pDataBuffer, DataLength);

if (pDataBuffer == NULL) {
DEBUGP(DL_FATAL, ("AllocRcvPkt: open %p, failed to alloc"
" data buffer %d bytes\n", pOpenContext, DataLength));
break;
}

//
// Make this an NDIS buffer.
//
NdisAllocateBuffer(
&Status,
&pNdisBuffer,
pOpenContext->RecvBufferPool,
pDataBuffer,
DataLength);

if (Status != NDIS_STATUS_SUCCESS) {
DEBUGP(DL_FATAL, ("AllocateRcvPkt: open %p, failed to alloc"
" NDIS buffer, %d bytes\n", pOpenContext, DataLength));
break;
}

NdisAllocatePacket(&Status, &pNdisPacket, pOpenContext->RecvPacketPool);

if (Status != NDIS_STATUS_SUCCESS) {
DEBUGP(DL_FATAL, ("AllocateRcvPkt: open %p, failed to alloc"
" NDIS packet, %d bytes\n", pOpenContext, DataLength));
break;
}

NDIS_SET_PACKET_STATUS(pNdisPacket, 0);
NPROT_RCV_PKT_TO_ORIGINAL_BUFFER(pNdisPacket) = NULL;

NdisChainBufferAtFront(pNdisPacket, pNdisBuffer);

*ppDataBuffer = pDataBuffer;


} while (FALSE);

if (pNdisPacket == NULL) {
//
// Clean up
//
if (pNdisBuffer != NULL) {
NdisFreeBuffer(pNdisBuffer);
}

if (pDataBuffer != NULL) {
NPROT_FREE_MEM(pDataBuffer);
}
}

return (pNdisPacket);
}



VOID
ndisprotFreeReceivePacket(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN PNDIS_PACKET pNdisPacket
)
/*++

Routine Description:

Free up all resources associated with a received packet. If this
is a local copy, free the packet to our receive pool, else return
this to the miniport.

Arguments:

pOpenContext - pointer to open context
pNdisPacket - pointer to packet to be freed.

Return Value:

None

--*/
{
PNDIS_BUFFER pNdisBuffer;
UINT TotalLength;
UINT BufferLength;
PUCHAR pCopyData;

if (NdisGetPoolFromPacket(pNdisPacket) == pOpenContext->RecvPacketPool) {
//
// This is a local copy.
//
#ifdef NDIS51
NdisGetFirstBufferFromPacketSafe(
pNdisPacket,
&pNdisBuffer,
(PVOID*)&pCopyData,
&BufferLength,
&TotalLength,
NormalPagePriority);
#else
NdisGetFirstBufferFromPacket(
pNdisPacket,
&pNdisBuffer,
(PVOID*)&pCopyData,
&BufferLength,
&TotalLength);
#endif

NPROT_ASSERT(BufferLength == TotalLength);

NPROT_ASSERT(pNdisBuffer != NULL);

NPROT_ASSERT(pCopyData != NULL); // we would have allocated non-paged pool

NdisFreePacket(pNdisPacket);

NdisFreeBuffer(pNdisBuffer);

NPROT_FREE_MEM(pCopyData);
}
else {
NdisReturnPackets(&pNdisPacket, 1);
}
}


VOID
ndisprotCancelPendingReads(
IN PNDISPROT_OPEN_CONTEXT pOpenContext
)
/*++

Routine Description:

Cancel any pending read IRPs queued on the given open.

Arguments:

pOpenContext - pointer to open context

Return Value:

None

--*/
{
PIRP pIrp;
PLIST_ENTRY pIrpEntry;

NPROT_REF_OPEN(pOpenContext); // temp ref - cancel reads

NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);

while (!NPROT_IS_LIST_EMPTY(&pOpenContext->PendedReads)) {
//
// Get the first pended Read IRP
//
pIrpEntry = pOpenContext->PendedReads.Flink;
pIrp = CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry);

//
// Check to see if it is being cancelled.
//
if (IoSetCancelRoutine(pIrp, NULL)) {
//
// It isn't being cancelled, and can't be cancelled henceforth.
//
NPROT_REMOVE_ENTRY_LIST(pIrpEntry);

NPROT_RELEASE_LOCK(&pOpenContext->Lock);

//
// Complete the IRP.
//
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;

DEBUGP(DL_INFO, ("CancelPendingReads: Open %p, IRP %p cancelled\n",
pOpenContext, pIrp));

IoCompleteRequest(pIrp, IO_NO_INCREMENT);

NPROT_DEREF_OPEN(pOpenContext); // took out pended Read for cancelling

NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
pOpenContext->PendedReadCount--;
}
else {
//
// It is being cancelled, let the cancel routine handle it.
//
NPROT_RELEASE_LOCK(&pOpenContext->Lock);

//
// Give the cancel routine some breathing space, otherwise
// we might end up examining the same (cancelled) IRP over
// and over again.
//
NPROT_SLEEP(1);

NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
}
}

NPROT_RELEASE_LOCK(&pOpenContext->Lock);

NPROT_DEREF_OPEN(pOpenContext); // temp ref - cancel reads
}


VOID
ndisprotFlushReceiveQueue(
IN PNDISPROT_OPEN_CONTEXT pOpenContext
)
/*++

Routine Description:

Free any receive packets queued up on the specified open

Arguments:

pOpenContext - pointer to open context

Return Value:

None

--*/
{
PLIST_ENTRY pRcvPacketEntry;
PNDIS_PACKET pRcvPacket;

NPROT_REF_OPEN(pOpenContext); // temp ref - flushRcvQueue

NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);

while (!NPROT_IS_LIST_EMPTY(&pOpenContext->RecvPktQueue)) {
//
// Get the first queued receive packet
//
pRcvPacketEntry = pOpenContext->RecvPktQueue.Flink;
NPROT_REMOVE_ENTRY_LIST(pRcvPacketEntry);

pOpenContext->RecvPktCount--;

NPROT_RELEASE_LOCK(&pOpenContext->Lock);

pRcvPacket = NPROT_LIST_ENTRY_TO_RCV_PKT(pRcvPacketEntry);

DEBUGP(DL_LOUD, ("FlushReceiveQueue: open %p, pkt %p\n",
pOpenContext, pRcvPacket));

ndisprotFreeReceivePacket(pOpenContext, pRcvPacket);

NPROT_DEREF_OPEN(pOpenContext); // took out pended Read

NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
}

NPROT_RELEASE_LOCK(&pOpenContext->Lock);

NPROT_DEREF_OPEN(pOpenContext); // temp ref - flushRcvQueue
}

send.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
/*++

Copyright (c) 2000 Microsoft Corporation

Module Name:

send.c

Abstract:

NDIS protocol entry points and utility routines to handle sending
data.

Environment:

Kernel mode only.

Revision History:

--*/

#include "precomp.h"

#define __FILENUMBER 'DNES'



// 分发函数,处理写请求(也就是发包请求)。
NTSTATUS NdisProtWrite(IN PDEVICE_OBJECT pDeviceObject,IN PIRP pIrp) {
PIO_STACK_LOCATION pIrpSp;
ULONG DataLength;
NTSTATUS NtStatus;
NDIS_STATUS Status;
PNDISPROT_OPEN_CONTEXT pOpenContext;
PNDIS_PACKET pNdisPacket;
PNDIS_BUFFER pNdisBuffer;
NDISPROT_ETH_HEADER UNALIGNED* pEthHeader;
// NDIS51支持写请求取消。但是本书不讨论请求取消的话题。
#ifdef NDIS51
PVOID CancelId;
#endif
UNREFERENCED_PARAMETER(pDeviceObject);
// 首先得到打开上下文。以确认是用哪个网卡发包。
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pOpenContext = pIrpSp->FileObject->FsContext;
pNdisPacket = NULL;
do {
// 检查打开上下文的可靠性。
if (pOpenContext == NULL) {
DEBUGP(DL_WARN, ("Write: FileObject %p not yet associated with a device\n", pIrpSp->FileObject));
NtStatus = STATUS_INVALID_HANDLE;
break;
};
NPROT_STRUCT_ASSERT(pOpenContext, oc);
// 确认输入缓冲的可靠性。
if (pIrp->MdlAddress == NULL) {
DEBUGP(DL_FATAL, ("Write: NULL MDL address on IRP %p\n", pIrp));
NtStatus = STATUS_INVALID_PARAMETER;
break;
};
// 得到输入缓冲的虚拟地址。之后进行一系列的检查。第一,输入缓冲虚拟地址不能为NULL,第二,缓冲的长度,至少必须比一个以太网包头要长。否则无法填写以太网包头。第三,发包的长度不能超过这个网卡的最大帧长。第四,
pEthHeader = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);
if (pEthHeader == NULL) {
DEBUGP(DL_FATAL, ("Write: MmGetSystemAddr failed for IRP %p, MDL %p\n", pIrp, pIrp->MdlAddress));
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
};
DataLength = MmGetMdlByteCount(pIrp->MdlAddress);
if (DataLength < sizeof(NDISPROT_ETH_HEADER)) {
DEBUGP(DL_WARN, ("Write: too small to be a valid packet (%d bytes)\n", DataLength));
NtStatus = STATUS_BUFFER_TOO_SMALL;
break;
};
if (DataLength > (pOpenContext->MaxFrameSize + sizeof(NDISPROT_ETH_HEADER))) {
DEBUGP(DL_WARN, ("Write: Open %p: data length (%d) larger than max frame size (%d)\n", pOpenContext, DataLength, pOpenContext->MaxFrameSize));
NtStatus = STATUS_INVALID_BUFFER_SIZE;
break;
};
// 下面开始检查,缓冲中是否已经填写了伪造的MAC地址。方法很简单,取得已填写的地址和网卡的MAC地址比较。如果不符合则返回失败。很多情况,网络攻击工具是不会拷贝这段代码的。
if ((pIrp->RequestorMode == UserMode) && !NPROT_MEM_CMP(pEthHeader->SrcAddr, pOpenContext->CurrentAddress, NPROT_MAC_ADDR_LEN)) {
DEBUGP(DL_WARN, ("Write: Failing with invalid Source address"));
NtStatus = STATUS_INVALID_PARAMETER;
break;
};
// 确认包可以发送了。下面开始真实的准备发送一个包,首先获得锁,并判断当前网卡是否处于可以发包的状态。
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_ACTIVE)) {
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
DEBUGP(DL_FATAL, ("Write: Open %p is not bound or in low power state\n", pOpenContext));
NtStatus = STATUS_INVALID_HANDLE;
break;
};
// 从前面绑定时分配的发送包池中分配一个包描述符。
NPROT_ASSERT(pOpenContext->SendPacketPool != NULL);
NdisAllocatePacket(&Status,&pNdisPacket,pOpenContext->SendPacketPool);
if (Status != NDIS_STATUS_SUCCESS) {
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
DEBUGP(DL_FATAL, ("Write: open %p, failed to alloc send pkt\n", pOpenContext));
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
};
// 下面的代码为Win9x编写,本书不讨论。
if (pOpenContext->bRunningOnWin9x) {
NdisAllocateBuffer(&Status,&pNdisBuffer,pOpenContext->SendBufferPool,pEthHeader,DataLength);
if (Status != NDIS_STATUS_SUCCESS) {
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
NdisFreePacket(pNdisPacket);
DEBUGP(DL_FATAL, ("Write: open %p, failed to alloc send buf\n", pOpenContext));
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
};
}
else
pNdisBuffer = pIrp->MdlAddress;
// 记录发送包又增加了一个。
NdisInterlockedIncrement((PLONG)&pOpenContext->PendedSendCount);
// 打开上下文引用计数加1,这是为了防止发包过程中这个绑定被解除。
NPROT_REF_OPEN(pOpenContext); // pended send
IoMarkIrpPending(pIrp);
// 初始化包引用计数。这个包会在计数为0的时候释放掉。
NPROT_SEND_PKT_RSVD(pNdisPacket)->RefCount = 1;
#ifdef NDIS51
// NDIS5.1支持取消发送。我们给每个包设置一个取消ID。每个包和一个写IRP关联,把包的指针保存在IRP中。如果IRP获得取消通知,则使用NdisCancelSendPackets去取消包。
CancelId = NPROT_GET_NEXT_CANCEL_ID();
NDIS_SET_PACKET_CANCEL_ID(pNdisPacket, CancelId);
pIrp->Tail.Overlay.DriverContext[0] = (PVOID)pOpenContext;
pIrp->Tail.Overlay.DriverContext[1] = (PVOID)pNdisPacket;
NPROT_INSERT_TAIL_LIST(&pOpenContext->PendedWrites, &pIrp->Tail.Overlay.ListEntry);
IoSetCancelRoutine(pIrp, NdisProtCancelWrite);
#endif // NDIS51
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
// 记下irp的指针放在包描述符里,以备后用。
NPROT_IRP_FROM_SEND_PKT(pNdisPacket) = pIrp;
// 把缓冲和包联系起来。状态设置为pending。
NtStatus = STATUS_PENDING;
pNdisBuffer->Next = NULL;
NdisChainBufferAtFront(pNdisPacket, pNdisBuffer);
// 下面的代码仅供调试使用。
#if SEND_DBG{
PUCHAR pData;
pData = MmGetSystemAddressForMdlSafe(pNdisBuffer, NormalPagePriority);
NPROT_ASSERT(pEthHeader == pData);
DEBUGP(DL_VERY_LOUD,("Write: MDL %p, MdlFlags %x, SystemAddr %p, %d bytes\n",pIrp->MdlAddress, pIrp->MdlAddress->MdlFlags, pData, DataLength));
DEBUGPDUMP(DL_VERY_LOUD, pData, MIN(DataLength, 48));
}
#endif // SEND_DBG
// 发送包。非常简单。包发送完之后会自动调用协议特征中的一个回调函数NdisProtSendComplete。在其中再完成IRP即可。
NdisSendPackets(pOpenContext->BindingHandle, &pNdisPacket, 1);
} while (FALSE);
// 如果正常发送包是STATUS_PENDING。否则是有错的,可以在这里直接完成。
if (NtStatus != STATUS_PENDING) {
pIrp->IoStatus.Status = NtStatus;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
};
return (NtStatus);
};


#ifdef NDIS51

VOID
NdisProtCancelWrite(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
/*++

Routine Description:

Cancel a pending write IRP. This routine attempt to cancel the NDIS send.

Arguments:

pDeviceObject - pointer to our device object
pIrp - IRP to be cancelled

Return Value:

None

--*/
{
PNDISPROT_OPEN_CONTEXT pOpenContext;
PLIST_ENTRY pIrpEntry;
PNDIS_PACKET pNdisPacket;

UNREFERENCED_PARAMETER(pDeviceObject);

IoReleaseCancelSpinLock(pIrp->CancelIrql);

//
// The NDIS packet representing this Write IRP.
//
pNdisPacket = NULL;

pOpenContext = (PNDISPROT_OPEN_CONTEXT)pIrp->Tail.Overlay.DriverContext[0];
NPROT_STRUCT_ASSERT(pOpenContext, oc);

//
// Try to locate the IRP in the pended write queue. The send completion
// routine may be running and might have removed it from there.
//
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);

for (pIrpEntry = pOpenContext->PendedWrites.Flink;
pIrpEntry != &pOpenContext->PendedWrites;
pIrpEntry = pIrpEntry->Flink) {
if (pIrp == CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry)) {
pNdisPacket = (PNDIS_PACKET)pIrp->Tail.Overlay.DriverContext[1];

//
// Place a reference on this packet so that it won't get
// freed/reused until we are done with it.
//
NPROT_REF_SEND_PKT(pNdisPacket);
break;
}
}

NPROT_RELEASE_LOCK(&pOpenContext->Lock);

if (pNdisPacket != NULL) {
//
// Either the send completion routine hasn't run, or we got a peak
// at the IRP/packet before it had a chance to take it out of the
// pending IRP queue.
//
// We do not complete the IRP here - note that we didn't dequeue it
// above. This is because we always want the send complete routine to
// complete the IRP. And this in turn is because the packet that was
// prepared from the IRP has a buffer chain pointing to data associated
// with this IRP. Therefore we cannot complete the IRP before the driver
// below us is done with the data it pointed to.
//

//
// Request NDIS to cancel this send. The result of this call is that
// our SendComplete handler will be called (if not already called).
//
DEBUGP(DL_INFO, ("CancelWrite: cancelling pkt %p on Open %p\n",
pNdisPacket, pOpenContext));
NdisCancelSendPackets(
pOpenContext->BindingHandle,
NDIS_GET_PACKET_CANCEL_ID(pNdisPacket)
);

//
// It is now safe to remove the reference we had placed on the packet.
//
NPROT_DEREF_SEND_PKT(pNdisPacket);
}
//
// else the send completion routine has already picked up this IRP.
//
}

#endif // NDIS51

// 这是协议特征集中的一个回调函数。如果调用了NdisSendPacket,那么在发送结束之后,这个函数会被调用。
VOID NdisProtSendComplete(IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET pNdisPacket, IN NDIS_STATUS Status) {
PIRP pIrp;
PIO_STACK_LOCATION pIrpSp;
PNDISPROT_OPEN_CONTEXT pOpenContext;
// 取得打开上下文。
pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext;
NPROT_STRUCT_ASSERT(pOpenContext, oc);
// 从包描述符中取得IRP的指针。
pIrp = NPROT_IRP_FROM_SEND_PKT(pNdisPacket);
// 下面的代码只和Win9x有关。本书不讨论。
if (pOpenContext->bRunningOnWin9x) {
// We would have attached our own NDIS_BUFFER. Take it out and free it.
#ifndef NDIS51
PNDIS_BUFFER pNdisBuffer;
PVOID VirtualAddr;
UINT BufferLength;
UINT TotalLength;
#endif
#ifdef NDIS51
NPROT_ASSERT(FALSE); // NDIS 5.1 not on Win9X!
#else
NdisGetFirstBufferFromPacket(pNdisPacket, &pNdisBuffer, &VirtualAddr, &BufferLength, &TotalLength);
NPROT_ASSERT(pNdisBuffer != NULL);
NdisFreeBuffer(pNdisBuffer);
#endif
};
// 去掉未决取消函数。同时从未决链中删除。
#ifdef NDIS51
IoSetCancelRoutine(pIrp, NULL);
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
NPROT_REMOVE_ENTRY_LIST(&pIrp->Tail.Overlay.ListEntry);
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
#endif
// 数据包解引用。
NPROT_DEREF_SEND_PKT(pNdisPacket);
// 把请求完成掉。
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
if (Status == NDIS_STATUS_SUCCESS) {
pIrp->IoStatus.Information = pIrpSp->Parameters.Write.Length;
pIrp->IoStatus.Status = STATUS_SUCCESS;
}
else {
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
};
DEBUGP(DL_INFO, ("SendComplete: packet %p/IRP %p/Length %d completed with status %x\n", pNdisPacket, pIrp, pIrp->IoStatus.Information, pIrp->IoStatus.Status));
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
// 未决发送包减少一个。
NdisInterlockedDecrement((PLONG)&pOpenContext->PendedSendCount);
NPROT_DEREF_OPEN(pOpenContext);
return;
};