xref: /reactos/drivers/network/tcpip/datalink/lan.c (revision 8a978a17)
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS TCP/IP protocol driver
4  * FILE:        datalink/lan.c
5  * PURPOSE:     Local Area Network media routines
6  * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7  * REVISIONS:
8  *   CSH 01/08-2000 Created
9  */
10 
11 #include "precomp.h"
12 
13 #include <ntifs.h>
14 #include <receive.h>
15 #include <wait.h>
16 
17 UINT TransferDataCalled = 0;
18 UINT TransferDataCompleteCalled = 0;
19 
20 #define CCS_ROOT L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet"
21 #define TCPIP_GUID L"{4D36E972-E325-11CE-BFC1-08002BE10318}"
22 
23 typedef struct _LAN_WQ_ITEM {
24     LIST_ENTRY ListEntry;
25     PNDIS_PACKET Packet;
26     PLAN_ADAPTER Adapter;
27     UINT BytesTransferred;
28     BOOLEAN LegacyReceive;
29 } LAN_WQ_ITEM, *PLAN_WQ_ITEM;
30 
31 typedef struct _RECONFIGURE_CONTEXT {
32     ULONG State;
33     PLAN_ADAPTER Adapter;
34 } RECONFIGURE_CONTEXT, *PRECONFIGURE_CONTEXT;
35 
36 NDIS_HANDLE NdisProtocolHandle = (NDIS_HANDLE)NULL;
37 BOOLEAN ProtocolRegistered     = FALSE;
38 LIST_ENTRY AdapterListHead;
39 KSPIN_LOCK AdapterListLock;
40 
41 NDIS_STATUS NDISCall(
42     PLAN_ADAPTER Adapter,
43     NDIS_REQUEST_TYPE Type,
44     NDIS_OID OID,
45     PVOID Buffer,
46     UINT Length)
47 /*
48  * FUNCTION: Send a request to NDIS
49  * ARGUMENTS:
50  *     Adapter     = Pointer to a LAN_ADAPTER structure
51  *     Type        = Type of request (Set or Query)
52  *     OID         = Value to be set/queried for
53  *     Buffer      = Pointer to a buffer to use
54  *     Length      = Number of bytes in Buffer
55  * RETURNS:
56  *     Status of operation
57  */
58 {
59     NDIS_REQUEST Request;
60     NDIS_STATUS NdisStatus;
61 
62     Request.RequestType = Type;
63     if (Type == NdisRequestSetInformation) {
64         Request.DATA.SET_INFORMATION.Oid                     = OID;
65         Request.DATA.SET_INFORMATION.InformationBuffer       = Buffer;
66         Request.DATA.SET_INFORMATION.InformationBufferLength = Length;
67     } else {
68         Request.DATA.QUERY_INFORMATION.Oid                     = OID;
69         Request.DATA.QUERY_INFORMATION.InformationBuffer       = Buffer;
70         Request.DATA.QUERY_INFORMATION.InformationBufferLength = Length;
71     }
72 
73     if (Adapter->State != LAN_STATE_RESETTING) {
74         NdisRequest(&NdisStatus, Adapter->NdisHandle, &Request);
75     } else {
76         NdisStatus = NDIS_STATUS_NOT_ACCEPTED;
77     }
78 
79     /* Wait for NDIS to complete the request */
80     if (NdisStatus == NDIS_STATUS_PENDING) {
81         KeWaitForSingleObject(&Adapter->Event,
82                               UserRequest,
83                               KernelMode,
84                               FALSE,
85                               NULL);
86         NdisStatus = Adapter->NdisStatus;
87     }
88 
89     return NdisStatus;
90 }
91 
92 /* Used by legacy ProtocolReceive for packet type */
93 NDIS_STATUS
94 GetPacketTypeFromHeaderBuffer(PLAN_ADAPTER Adapter,
95                               PVOID HeaderBuffer,
96                               ULONG HeaderBufferSize,
97                               PULONG PacketType)
98 {
99     PETH_HEADER EthHeader = HeaderBuffer;
100 
101     if (HeaderBufferSize < Adapter->HeaderSize)
102     {
103         TI_DbgPrint(DEBUG_DATALINK, ("Runt frame (size %d).\n", HeaderBufferSize));
104         return NDIS_STATUS_NOT_ACCEPTED;
105     }
106 
107     switch (Adapter->Media)
108     {
109         case NdisMedium802_3:
110             /* Ethernet and IEEE 802.3 frames can be distinguished by
111                looking at the IEEE 802.3 length field. This field is
112                less than or equal to 1500 for a valid IEEE 802.3 frame
113                and larger than 1500 is it's a valid EtherType value.
114                See RFC 1122, section 2.3.3 for more information */
115 
116             *PacketType = EthHeader->EType;
117             break;
118 
119         default:
120             TI_DbgPrint(MIN_TRACE, ("Unsupported media.\n"));
121 
122             /* FIXME: Support other medias */
123             return NDIS_STATUS_NOT_ACCEPTED;
124     }
125 
126     TI_DbgPrint(DEBUG_DATALINK, ("EtherType (0x%X).\n", *PacketType));
127 
128     return NDIS_STATUS_SUCCESS;
129 }
130 
131 /* Used by ProtocolReceivePacket for packet type */
132 NDIS_STATUS
133 GetPacketTypeFromNdisPacket(PLAN_ADAPTER Adapter,
134                             PNDIS_PACKET NdisPacket,
135                             PULONG PacketType)
136 {
137     PVOID HeaderBuffer;
138     ULONG BytesCopied;
139     NDIS_STATUS Status;
140 
141     HeaderBuffer = ExAllocatePoolWithTag(NonPagedPool,
142                                          Adapter->HeaderSize,
143                                          HEADER_TAG);
144     if (!HeaderBuffer)
145         return NDIS_STATUS_RESOURCES;
146 
147     /* Copy the media header */
148     BytesCopied = CopyPacketToBuffer(HeaderBuffer,
149                                      NdisPacket,
150                                      0,
151                                      Adapter->HeaderSize);
152     if (BytesCopied != Adapter->HeaderSize)
153     {
154         /* Runt frame */
155         ExFreePoolWithTag(HeaderBuffer, HEADER_TAG);
156         TI_DbgPrint(DEBUG_DATALINK, ("Runt frame (size %d).\n", BytesCopied));
157         return NDIS_STATUS_NOT_ACCEPTED;
158     }
159 
160     Status = GetPacketTypeFromHeaderBuffer(Adapter,
161                                            HeaderBuffer,
162                                            BytesCopied,
163                                            PacketType);
164 
165     ExFreePoolWithTag(HeaderBuffer, HEADER_TAG);
166 
167     return Status;
168 }
169 
170 
171 VOID FreeAdapter(
172     PLAN_ADAPTER Adapter)
173 /*
174  * FUNCTION: Frees memory for a LAN_ADAPTER structure
175  * ARGUMENTS:
176  *     Adapter = Pointer to LAN_ADAPTER structure to free
177  */
178 {
179     ExFreePoolWithTag(Adapter, LAN_ADAPTER_TAG);
180 }
181 
182 
183 NTSTATUS TcpipLanGetDwordOid
184 ( PIP_INTERFACE Interface,
185   NDIS_OID Oid,
186   PULONG Result ) {
187     /* Get maximum frame size */
188     if( Interface->Context ) {
189         return NDISCall((PLAN_ADAPTER)Interface->Context,
190                         NdisRequestQueryInformation,
191                         Oid,
192                         Result,
193                         sizeof(ULONG));
194     } else switch( Oid ) { /* Loopback Case */
195     case OID_GEN_HARDWARE_STATUS:
196         *Result = NdisHardwareStatusReady;
197         return STATUS_SUCCESS;
198     case OID_GEN_MEDIA_CONNECT_STATUS:
199         *Result = NdisMediaStateConnected;
200         return STATUS_SUCCESS;
201     default:
202         return STATUS_INVALID_PARAMETER;
203     }
204 }
205 
206 
207 VOID NTAPI ProtocolOpenAdapterComplete(
208     NDIS_HANDLE BindingContext,
209     NDIS_STATUS Status,
210     NDIS_STATUS OpenErrorStatus)
211 /*
212  * FUNCTION: Called by NDIS to complete opening of an adapter
213  * ARGUMENTS:
214  *     BindingContext  = Pointer to a device context (LAN_ADAPTER)
215  *     Status          = Status of the operation
216  *     OpenErrorStatus = Additional status information
217  */
218 {
219     PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext;
220 
221     TI_DbgPrint(DEBUG_DATALINK, ("Called.\n"));
222 
223     Adapter->NdisStatus = Status;
224 
225     KeSetEvent(&Adapter->Event, 0, FALSE);
226 }
227 
228 
229 VOID NTAPI ProtocolCloseAdapterComplete(
230     NDIS_HANDLE BindingContext,
231     NDIS_STATUS Status)
232 /*
233  * FUNCTION: Called by NDIS to complete closing an adapter
234  * ARGUMENTS:
235  *     BindingContext = Pointer to a device context (LAN_ADAPTER)
236  *     Status         = Status of the operation
237  */
238 {
239     PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext;
240 
241     TI_DbgPrint(DEBUG_DATALINK, ("Called.\n"));
242 
243     Adapter->NdisStatus = Status;
244 
245     KeSetEvent(&Adapter->Event, 0, FALSE);
246 }
247 
248 
249 VOID NTAPI ProtocolResetComplete(
250     NDIS_HANDLE BindingContext,
251     NDIS_STATUS Status)
252 /*
253  * FUNCTION: Called by NDIS to complete resetting an adapter
254  * ARGUMENTS:
255  *     BindingContext = Pointer to a device context (LAN_ADAPTER)
256  *     Status         = Status of the operation
257  */
258 {
259     PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext;
260 
261     TI_DbgPrint(DEBUG_DATALINK, ("Called.\n"));
262 
263     Adapter->NdisStatus = Status;
264 
265     KeSetEvent(&Adapter->Event, 0, FALSE);
266 }
267 
268 
269 VOID NTAPI ProtocolRequestComplete(
270     NDIS_HANDLE BindingContext,
271     PNDIS_REQUEST NdisRequest,
272     NDIS_STATUS Status)
273 /*
274  * FUNCTION: Called by NDIS to complete a request
275  * ARGUMENTS:
276  *     BindingContext = Pointer to a device context (LAN_ADAPTER)
277  *     NdisRequest    = Pointer to an object describing the request
278  *     Status         = Status of the operation
279  */
280 {
281     PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext;
282 
283     TI_DbgPrint(DEBUG_DATALINK, ("Called.\n"));
284 
285     /* Save status of request and signal an event */
286     Adapter->NdisStatus = Status;
287 
288     KeSetEvent(&Adapter->Event, 0, FALSE);
289 }
290 
291 
292 VOID NTAPI ProtocolSendComplete(
293     NDIS_HANDLE BindingContext,
294     PNDIS_PACKET Packet,
295     NDIS_STATUS Status)
296 /*
297  * FUNCTION: Called by NDIS to complete sending process
298  * ARGUMENTS:
299  *     BindingContext = Pointer to a device context (LAN_ADAPTER)
300  *     Packet         = Pointer to a packet descriptor
301  *     Status         = Status of the operation
302  */
303 {
304     FreeNdisPacket(Packet);
305 }
306 
307 VOID LanReceiveWorker( PVOID Context ) {
308     ULONG PacketType;
309     PLAN_WQ_ITEM WorkItem = (PLAN_WQ_ITEM)Context;
310     PNDIS_PACKET Packet;
311     PLAN_ADAPTER Adapter;
312     UINT BytesTransferred;
313     IP_PACKET IPPacket;
314     BOOLEAN LegacyReceive;
315     PIP_INTERFACE Interface;
316 
317     TI_DbgPrint(DEBUG_DATALINK, ("Called.\n"));
318 
319     Packet = WorkItem->Packet;
320     Adapter = WorkItem->Adapter;
321     BytesTransferred = WorkItem->BytesTransferred;
322     LegacyReceive = WorkItem->LegacyReceive;
323 
324     ExFreePoolWithTag(WorkItem, WQ_CONTEXT_TAG);
325 
326     Interface = Adapter->Context;
327 
328     IPInitializePacket(&IPPacket, 0);
329 
330     IPPacket.NdisPacket = Packet;
331     IPPacket.ReturnPacket = !LegacyReceive;
332 
333     if (LegacyReceive)
334     {
335         /* Packet type is precomputed */
336         PacketType = PC(IPPacket.NdisPacket)->PacketType;
337 
338         /* Data is at position 0 */
339         IPPacket.Position = 0;
340 
341         /* Packet size is determined by bytes transferred */
342         IPPacket.TotalSize = BytesTransferred;
343     }
344     else
345     {
346         /* Determine packet type from media header */
347         if (GetPacketTypeFromNdisPacket(Adapter,
348                                         IPPacket.NdisPacket,
349                                         &PacketType) != NDIS_STATUS_SUCCESS)
350         {
351             /* Bad packet */
352             IPPacket.Free(&IPPacket);
353             return;
354         }
355 
356         /* Data is at the end of the media header */
357         IPPacket.Position = Adapter->HeaderSize;
358 
359         /* Calculate packet size (excluding media header) */
360         NdisQueryPacketLength(IPPacket.NdisPacket, &IPPacket.TotalSize);
361     }
362 
363     TI_DbgPrint
364 	(DEBUG_DATALINK,
365 	 ("Ether Type = %x Total = %d\n",
366 	  PacketType, IPPacket.TotalSize));
367 
368     /* Update interface stats */
369     Interface->Stats.InBytes += IPPacket.TotalSize + Adapter->HeaderSize;
370 
371     /* NDIS packet is freed in all of these cases */
372     switch (PacketType) {
373         case ETYPE_IPv4:
374         case ETYPE_IPv6:
375             TI_DbgPrint(MID_TRACE,("Received IP Packet\n"));
376             IPReceive(Adapter->Context, &IPPacket);
377             break;
378         case ETYPE_ARP:
379             TI_DbgPrint(MID_TRACE,("Received ARP Packet\n"));
380             ARPReceive(Adapter->Context, &IPPacket);
381             break;
382         default:
383             IPPacket.Free(&IPPacket);
384             break;
385     }
386 }
387 
388 VOID LanSubmitReceiveWork(
389     NDIS_HANDLE BindingContext,
390     PNDIS_PACKET Packet,
391     UINT BytesTransferred,
392     BOOLEAN LegacyReceive) {
393     PLAN_WQ_ITEM WQItem = ExAllocatePoolWithTag(NonPagedPool, sizeof(LAN_WQ_ITEM),
394                                                 WQ_CONTEXT_TAG);
395     PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext;
396 
397     TI_DbgPrint(DEBUG_DATALINK,("called\n"));
398 
399     if (!WQItem) return;
400 
401     WQItem->Packet = Packet;
402     WQItem->Adapter = Adapter;
403     WQItem->BytesTransferred = BytesTransferred;
404     WQItem->LegacyReceive = LegacyReceive;
405 
406     if (!ChewCreate( LanReceiveWorker, WQItem ))
407         ExFreePoolWithTag(WQItem, WQ_CONTEXT_TAG);
408 }
409 
410 VOID NTAPI ProtocolTransferDataComplete(
411     NDIS_HANDLE BindingContext,
412     PNDIS_PACKET Packet,
413     NDIS_STATUS Status,
414     UINT BytesTransferred)
415 /*
416  * FUNCTION: Called by NDIS to complete reception of data
417  * ARGUMENTS:
418  *     BindingContext   = Pointer to a device context (LAN_ADAPTER)
419  *     Packet           = Pointer to a packet descriptor
420  *     Status           = Status of the operation
421  *     BytesTransferred = Number of bytes transferred
422  * NOTES:
423  *     If the packet was successfully received, determine the protocol
424  *     type and pass it to the correct receive handler
425  */
426 {
427     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
428 
429     TI_DbgPrint(DEBUG_DATALINK,("called\n"));
430 
431     TransferDataCompleteCalled++;
432     ASSERT(TransferDataCompleteCalled <= TransferDataCalled);
433 
434     if( Status != NDIS_STATUS_SUCCESS ) return;
435 
436     LanSubmitReceiveWork(BindingContext,
437                          Packet,
438                          BytesTransferred,
439                          TRUE);
440 }
441 
442 INT NTAPI ProtocolReceivePacket(
443     NDIS_HANDLE BindingContext,
444     PNDIS_PACKET NdisPacket)
445 {
446     PLAN_ADAPTER Adapter = BindingContext;
447 
448     if (Adapter->State != LAN_STATE_STARTED) {
449         TI_DbgPrint(DEBUG_DATALINK, ("Adapter is stopped.\n"));
450         return 0;
451     }
452 
453     LanSubmitReceiveWork(BindingContext,
454                          NdisPacket,
455                          0, /* Unused */
456                          FALSE);
457 
458     /* Hold 1 reference on this packet */
459     return 1;
460 }
461 
462 NDIS_STATUS NTAPI ProtocolReceive(
463     NDIS_HANDLE BindingContext,
464     NDIS_HANDLE MacReceiveContext,
465     PVOID HeaderBuffer,
466     UINT HeaderBufferSize,
467     PVOID LookaheadBuffer,
468     UINT LookaheadBufferSize,
469     UINT PacketSize)
470 /*
471  * FUNCTION: Called by NDIS when a packet has been received on the physical link
472  * ARGUMENTS:
473  *     BindingContext      = Pointer to a device context (LAN_ADAPTER)
474  *     MacReceiveContext   = Handle used by underlying NIC driver
475  *     HeaderBuffer        = Pointer to a buffer containing the packet header
476  *     HeaderBufferSize    = Number of bytes in HeaderBuffer
477  *     LookaheadBuffer     = Pointer to a buffer containing buffered packet data
478  *     LookaheadBufferSize = Size of LookaheadBuffer. May be less than asked for
479  *     PacketSize          = Overall size of the packet (not including header)
480  * RETURNS:
481  *     Status of operation
482  */
483 {
484     ULONG PacketType;
485     UINT BytesTransferred;
486     PCHAR BufferData;
487     NDIS_STATUS NdisStatus;
488     PNDIS_PACKET NdisPacket;
489     PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext;
490 
491     TI_DbgPrint(DEBUG_DATALINK, ("Called. (packetsize %d)\n",PacketSize));
492 
493     if (Adapter->State != LAN_STATE_STARTED) {
494         TI_DbgPrint(DEBUG_DATALINK, ("Adapter is stopped.\n"));
495         return NDIS_STATUS_NOT_ACCEPTED;
496     }
497 
498     if (HeaderBufferSize < Adapter->HeaderSize) {
499         TI_DbgPrint(DEBUG_DATALINK, ("Runt frame received.\n"));
500         return NDIS_STATUS_NOT_ACCEPTED;
501     }
502 
503     NdisStatus = GetPacketTypeFromHeaderBuffer(Adapter,
504                                                HeaderBuffer,
505                                                HeaderBufferSize,
506                                                &PacketType);
507     if (NdisStatus != NDIS_STATUS_SUCCESS)
508         return NDIS_STATUS_NOT_ACCEPTED;
509 
510     TI_DbgPrint(DEBUG_DATALINK, ("Adapter: %x (MTU %d)\n",
511 				 Adapter, Adapter->MTU));
512 
513     /* Get a transfer data packet */
514     NdisStatus = AllocatePacketWithBuffer( &NdisPacket, NULL,
515                                            PacketSize );
516     if( NdisStatus != NDIS_STATUS_SUCCESS ) {
517 	return NDIS_STATUS_NOT_ACCEPTED;
518     }
519 
520     PC(NdisPacket)->PacketType = PacketType;
521 
522     TI_DbgPrint(DEBUG_DATALINK, ("pretransfer LookaheadBufferSize %d packsize %d\n",LookaheadBufferSize,PacketSize));
523 
524     GetDataPtr( NdisPacket, 0, &BufferData, &PacketSize );
525 
526     TransferDataCalled++;
527 
528     if (LookaheadBufferSize == PacketSize)
529     {
530         /* Optimized code path for packets that are fully contained in
531          * the lookahead buffer. */
532         NdisCopyLookaheadData(BufferData,
533                               LookaheadBuffer,
534                               LookaheadBufferSize,
535                               Adapter->MacOptions);
536     }
537     else
538     {
539         NdisTransferData(&NdisStatus, Adapter->NdisHandle,
540                          MacReceiveContext, 0, PacketSize,
541 			 NdisPacket, &BytesTransferred);
542     }
543     TI_DbgPrint(DEBUG_DATALINK, ("Calling complete\n"));
544 
545     if (NdisStatus != NDIS_STATUS_PENDING)
546 	ProtocolTransferDataComplete(BindingContext,
547 				     NdisPacket,
548 				     NdisStatus,
549 				     PacketSize);
550 
551     TI_DbgPrint(DEBUG_DATALINK, ("leaving\n"));
552 
553     return NDIS_STATUS_SUCCESS;
554 }
555 
556 
557 VOID NTAPI ProtocolReceiveComplete(
558     NDIS_HANDLE BindingContext)
559 /*
560  * FUNCTION: Called by NDIS when we're done receiving data
561  * ARGUMENTS:
562  *     BindingContext = Pointer to a device context (LAN_ADAPTER)
563  */
564 {
565     TI_DbgPrint(DEBUG_DATALINK, ("Called.\n"));
566 }
567 
568 BOOLEAN ReadIpConfiguration(PIP_INTERFACE Interface)
569 {
570     OBJECT_ATTRIBUTES ObjectAttributes;
571     HANDLE ParameterHandle;
572     PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
573     ULONG KeyValueInfoLength;
574     WCHAR Buffer[150];
575     UNICODE_STRING IPAddress = RTL_CONSTANT_STRING(L"IPAddress");
576     UNICODE_STRING Netmask = RTL_CONSTANT_STRING(L"SubnetMask");
577     UNICODE_STRING Gateway = RTL_CONSTANT_STRING(L"DefaultGateway");
578     UNICODE_STRING EnableDhcp = RTL_CONSTANT_STRING(L"EnableDHCP");
579     UNICODE_STRING Prefix = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\");
580     UNICODE_STRING TcpipRegistryPath;
581     UNICODE_STRING RegistryDataU;
582     ANSI_STRING RegistryDataA;
583     ULONG Unused;
584     NTSTATUS Status;
585     IP_ADDRESS DefaultMask, Router;
586 
587     AddrInitIPv4(&DefaultMask, 0);
588 
589     TcpipRegistryPath.MaximumLength = sizeof(WCHAR) * 150;
590     TcpipRegistryPath.Length = 0;
591     TcpipRegistryPath.Buffer = Buffer;
592 
593     /* Build the registry path */
594     RtlAppendUnicodeStringToString(&TcpipRegistryPath, &Prefix);
595     RtlAppendUnicodeStringToString(&TcpipRegistryPath, &Interface->Name);
596 
597     InitializeObjectAttributes(&ObjectAttributes,
598                                &TcpipRegistryPath,
599                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
600                                0,
601                                NULL);
602 
603     /* Open a handle to the adapter parameters */
604     Status = ZwOpenKey(&ParameterHandle, KEY_READ, &ObjectAttributes);
605 
606     if (!NT_SUCCESS(Status))
607     {
608         return FALSE;
609     }
610     else
611     {
612         KeyValueInfoLength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + 16 * sizeof(WCHAR);
613         KeyValueInfo = ExAllocatePoolWithTag(PagedPool,
614                                              KeyValueInfoLength,
615                                              KEY_VALUE_TAG);
616         if (!KeyValueInfo)
617         {
618             ZwClose(ParameterHandle);
619             return FALSE;
620         }
621 
622         /* Read the EnableDHCP entry */
623         Status = ZwQueryValueKey(ParameterHandle,
624                                  &EnableDhcp,
625                                  KeyValuePartialInformation,
626                                  KeyValueInfo,
627                                  KeyValueInfoLength,
628                                  &Unused);
629         if (NT_SUCCESS(Status) && KeyValueInfo->DataLength == sizeof(ULONG) && (*(PULONG)KeyValueInfo->Data) == 0)
630         {
631             RegistryDataU.MaximumLength = KeyValueInfoLength - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
632             RegistryDataU.Buffer = (PWCHAR)KeyValueInfo->Data;
633 
634             /* Read the IP address */
635             Status = ZwQueryValueKey(ParameterHandle,
636                                      &IPAddress,
637                                      KeyValuePartialInformation,
638                                      KeyValueInfo,
639                                      KeyValueInfoLength,
640                                      &Unused);
641             if (NT_SUCCESS(Status))
642             {
643                 RegistryDataU.Length = KeyValueInfo->DataLength;
644 
645                 Status = RtlUnicodeStringToAnsiString(&RegistryDataA,
646                                                       &RegistryDataU,
647                                                       TRUE);
648                 if (NT_SUCCESS(Status))
649                 {
650                     AddrInitIPv4(&Interface->Unicast,
651                                  inet_addr(RegistryDataA.Buffer));
652                     RtlFreeAnsiString(&RegistryDataA);
653                 }
654             }
655 
656             Status = ZwQueryValueKey(ParameterHandle,
657                                      &Netmask,
658                                      KeyValuePartialInformation,
659                                      KeyValueInfo,
660                                      KeyValueInfoLength,
661                                      &Unused);
662             if (NT_SUCCESS(Status))
663             {
664                 RegistryDataU.Length = KeyValueInfo->DataLength;
665 
666                 Status = RtlUnicodeStringToAnsiString(&RegistryDataA,
667                                                       &RegistryDataU,
668                                                       TRUE);
669                 if (NT_SUCCESS(Status))
670                 {
671                     AddrInitIPv4(&Interface->Netmask,
672                                  inet_addr(RegistryDataA.Buffer));
673                     RtlFreeAnsiString(&RegistryDataA);
674                 }
675             }
676 
677             /* We have to wait until both IP address and subnet mask
678              * are read to add the interface route, but we must do it
679              * before we add the default gateway */
680             if (!AddrIsUnspecified(&Interface->Unicast) &&
681                 !AddrIsUnspecified(&Interface->Netmask))
682                 IPAddInterfaceRoute(Interface);
683 
684             /* Read default gateway info */
685             Status = ZwQueryValueKey(ParameterHandle,
686                                      &Gateway,
687                                      KeyValuePartialInformation,
688                                      KeyValueInfo,
689                                      KeyValueInfoLength,
690                                      &Unused);
691             if (NT_SUCCESS(Status))
692             {
693                 RegistryDataU.Length = KeyValueInfo->DataLength;
694 
695                 Status = RtlUnicodeStringToAnsiString(&RegistryDataA,
696                                                       &RegistryDataU,
697                                                       TRUE);
698                 if (NT_SUCCESS(Status))
699                 {
700                     AddrInitIPv4(&Router, inet_addr(RegistryDataA.Buffer));
701 
702                     if (!AddrIsUnspecified(&Router))
703                         RouterCreateRoute(&DefaultMask, &DefaultMask, &Router, Interface, 1);
704 
705                     RtlFreeAnsiString(&RegistryDataA);
706                 }
707             }
708         }
709 
710         ExFreePoolWithTag(KeyValueInfo, KEY_VALUE_TAG);
711         ZwClose(ParameterHandle);
712     }
713 
714     return TRUE;
715 }
716 
717 BOOLEAN ReconfigureAdapter(PRECONFIGURE_CONTEXT Context)
718 {
719     PLAN_ADAPTER Adapter = Context->Adapter;
720     PIP_INTERFACE Interface = Adapter->Context;
721     //NDIS_STATUS NdisStatus;
722     IP_ADDRESS DefaultMask;
723 
724     /* Initialize the default unspecified address (0.0.0.0) */
725     AddrInitIPv4(&DefaultMask, 0);
726     if (Context->State == LAN_STATE_STARTED &&
727         !Context->Adapter->CompletingReset)
728     {
729         /* Read the IP configuration */
730         ReadIpConfiguration(Interface);
731 
732         /* Compute the broadcast address */
733         Interface->Broadcast.Type = IP_ADDRESS_V4;
734         Interface->Broadcast.Address.IPv4Address = Interface->Unicast.Address.IPv4Address |
735                                                   ~Interface->Netmask.Address.IPv4Address;
736     }
737     else if (!Context->Adapter->CompletingReset)
738     {
739         /* Clear IP configuration */
740         Interface->Unicast = DefaultMask;
741         Interface->Netmask = DefaultMask;
742         Interface->Broadcast = DefaultMask;
743 
744         /* Remove all interface routes */
745         RouterRemoveRoutesForInterface(Interface);
746 
747         /* Destroy all cached neighbors */
748         NBDestroyNeighborsForInterface(Interface);
749     }
750 
751     Context->Adapter->CompletingReset = FALSE;
752 
753     /* Update the IP and link status information cached in TCP */
754     TCPUpdateInterfaceIPInformation(Interface);
755     TCPUpdateInterfaceLinkStatus(Interface);
756 
757     /* We're done here if the adapter isn't connected */
758     if (Context->State != LAN_STATE_STARTED)
759     {
760         Adapter->State = Context->State;
761         return TRUE;
762     }
763 
764     /* NDIS Bug! */
765 #if 0
766     /* Get maximum link speed */
767     NdisStatus = NDISCall(Adapter,
768                           NdisRequestQueryInformation,
769                           OID_GEN_LINK_SPEED,
770                           &Interface->Speed,
771                           sizeof(UINT));
772 
773     if (!NT_SUCCESS(NdisStatus))
774         Interface->Speed = IP_DEFAULT_LINK_SPEED;
775 
776     Adapter->Speed = Interface->Speed * 100L;
777 
778     /* Get maximum frame size */
779     NdisStatus = NDISCall(Adapter,
780                           NdisRequestQueryInformation,
781                           OID_GEN_MAXIMUM_FRAME_SIZE,
782                           &Adapter->MTU,
783                           sizeof(UINT));
784     if (NdisStatus != NDIS_STATUS_SUCCESS)
785         return FALSE;
786 
787     Interface->MTU = Adapter->MTU;
788 
789     /* Get maximum packet size */
790     NdisStatus = NDISCall(Adapter,
791                           NdisRequestQueryInformation,
792                           OID_GEN_MAXIMUM_TOTAL_SIZE,
793                           &Adapter->MaxPacketSize,
794                           sizeof(UINT));
795     if (NdisStatus != NDIS_STATUS_SUCCESS)
796         return FALSE;
797 #endif
798 
799     Adapter->State = Context->State;
800 
801     return TRUE;
802 }
803 
804 VOID ReconfigureAdapterWorker(PVOID Context)
805 {
806     PRECONFIGURE_CONTEXT ReconfigureContext = Context;
807 
808     /* Complete the reconfiguration asynchronously */
809     ReconfigureAdapter(ReconfigureContext);
810 
811     /* Free the context */
812     ExFreePool(ReconfigureContext);
813 }
814 
815 VOID NTAPI ProtocolStatus(
816     NDIS_HANDLE BindingContext,
817     NDIS_STATUS GeneralStatus,
818     PVOID StatusBuffer,
819     UINT StatusBufferSize)
820 /*
821  * FUNCTION: Called by NDIS when the underlying driver has changed state
822  * ARGUMENTS:
823  *     BindingContext   = Pointer to a device context (LAN_ADAPTER)
824  *     GeneralStatus    = A general status code
825  *     StatusBuffer     = Pointer to a buffer with medium-specific data
826  *     StatusBufferSize = Number of bytes in StatusBuffer
827  */
828 {
829     PLAN_ADAPTER Adapter = BindingContext;
830     PRECONFIGURE_CONTEXT Context;
831 
832     TI_DbgPrint(DEBUG_DATALINK, ("Called.\n"));
833 
834     /* Ignore the status indication if we have no context yet. We'll get another later */
835     if (!Adapter->Context)
836         return;
837 
838     Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(RECONFIGURE_CONTEXT), CONTEXT_TAG);
839     if (!Context)
840         return;
841 
842     Context->Adapter = Adapter;
843 
844     switch(GeneralStatus)
845     {
846         case NDIS_STATUS_MEDIA_CONNECT:
847             DbgPrint("NDIS_STATUS_MEDIA_CONNECT\n");
848 
849             if (Adapter->State == LAN_STATE_STARTED)
850             {
851                 ExFreePoolWithTag(Context, CONTEXT_TAG);
852                 return;
853             }
854 
855             Context->State = LAN_STATE_STARTED;
856             break;
857 
858         case NDIS_STATUS_MEDIA_DISCONNECT:
859             DbgPrint("NDIS_STATUS_MEDIA_DISCONNECT\n");
860 
861             if (Adapter->State == LAN_STATE_STOPPED)
862             {
863                 ExFreePoolWithTag(Context, CONTEXT_TAG);
864                 return;
865             }
866 
867             Context->State = LAN_STATE_STOPPED;
868             break;
869 
870         case NDIS_STATUS_RESET_START:
871             Adapter->OldState = Adapter->State;
872             Adapter->State = LAN_STATE_RESETTING;
873             /* Nothing else to do here */
874             ExFreePoolWithTag(Context, CONTEXT_TAG);
875             return;
876 
877         case NDIS_STATUS_RESET_END:
878             Adapter->CompletingReset = TRUE;
879             Context->State = Adapter->OldState;
880             break;
881 
882         default:
883             DbgPrint("Unhandled status: %x", GeneralStatus);
884             ExFreePoolWithTag(Context, CONTEXT_TAG);
885             return;
886     }
887 
888     /* Queue the work item */
889     if (!ChewCreate(ReconfigureAdapterWorker, Context))
890         ExFreePoolWithTag(Context, CONTEXT_TAG);
891 }
892 
893 VOID NTAPI ProtocolStatusComplete(NDIS_HANDLE NdisBindingContext)
894 /*
895  * FUNCTION: Called by NDIS when a status-change has occurred
896  * ARGUMENTS:
897  *     BindingContext = Pointer to a device context (LAN_ADAPTER)
898  */
899 {
900     TI_DbgPrint(DEBUG_DATALINK, ("Called.\n"));
901 }
902 
903 NDIS_STATUS NTAPI
904 ProtocolPnPEvent(
905     NDIS_HANDLE NdisBindingContext,
906     PNET_PNP_EVENT PnPEvent)
907 {
908     switch(PnPEvent->NetEvent)
909     {
910       case NetEventSetPower:
911          DbgPrint("Device transitioned to power state %ld\n", PnPEvent->Buffer);
912          return NDIS_STATUS_SUCCESS;
913 
914       case NetEventQueryPower:
915          DbgPrint("Device wants to go into power state %ld\n", PnPEvent->Buffer);
916          return NDIS_STATUS_SUCCESS;
917 
918       case NetEventQueryRemoveDevice:
919          DbgPrint("Device is about to be removed\n");
920          return NDIS_STATUS_SUCCESS;
921 
922       case NetEventCancelRemoveDevice:
923          DbgPrint("Device removal cancelled\n");
924          return NDIS_STATUS_SUCCESS;
925 
926       default:
927          DbgPrint("Unhandled event type: %ld\n", PnPEvent->NetEvent);
928          return NDIS_STATUS_SUCCESS;
929     }
930 }
931 
932 VOID NTAPI ProtocolBindAdapter(
933     OUT PNDIS_STATUS   Status,
934     IN  NDIS_HANDLE    BindContext,
935     IN  PNDIS_STRING   DeviceName,
936     IN  PVOID          SystemSpecific1,
937     IN  PVOID          SystemSpecific2)
938 /*
939  * FUNCTION: Called by NDIS during NdisRegisterProtocol to set up initial
940  *           bindings, and periodically thereafter as new adapters come online
941  * ARGUMENTS:
942  *     Status: Return value to NDIS
943  *     BindContext: Handle provided by NDIS to track pending binding operations
944  *     DeviceName: Name of the miniport device to bind to
945  *     SystemSpecific1: Pointer to a registry path with protocol-specific configuration information
946  *     SystemSpecific2: Unused & must not be touched
947  */
948 {
949     TI_DbgPrint(DEBUG_DATALINK, ("Called with registry path %wZ for %wZ\n", SystemSpecific1, DeviceName));
950     *Status = LANRegisterAdapter(DeviceName, SystemSpecific1);
951 }
952 
953 
954 VOID LANTransmit(
955     PVOID Context,
956     PNDIS_PACKET NdisPacket,
957     UINT Offset,
958     PVOID LinkAddress,
959     USHORT Type)
960 /*
961  * FUNCTION: Transmits a packet
962  * ARGUMENTS:
963  *     Context     = Pointer to context information (LAN_ADAPTER)
964  *     NdisPacket  = Pointer to NDIS packet to send
965  *     Offset      = Offset in packet where data starts
966  *     LinkAddress = Pointer to link address of destination (NULL = broadcast)
967  *     Type        = LAN protocol type (LAN_PROTO_*)
968  */
969 {
970     NDIS_STATUS NdisStatus;
971     PETH_HEADER EHeader;
972     PCHAR Data, OldData;
973     UINT Size, OldSize;
974     PLAN_ADAPTER Adapter = (PLAN_ADAPTER)Context;
975     KIRQL OldIrql;
976     PNDIS_PACKET XmitPacket;
977     PIP_INTERFACE Interface = Adapter->Context;
978 
979     TI_DbgPrint(DEBUG_DATALINK,
980 		("Called( NdisPacket %x, Offset %d, Adapter %x )\n",
981 		 NdisPacket, Offset, Adapter));
982 
983     if (Adapter->State != LAN_STATE_STARTED) {
984         (*PC(NdisPacket)->DLComplete)(PC(NdisPacket)->Context, NdisPacket, NDIS_STATUS_NOT_ACCEPTED);
985         return;
986     }
987 
988     TI_DbgPrint(DEBUG_DATALINK,
989 		("Adapter Address [%02x %02x %02x %02x %02x %02x]\n",
990 		 Adapter->HWAddress[0] & 0xff,
991 		 Adapter->HWAddress[1] & 0xff,
992 		 Adapter->HWAddress[2] & 0xff,
993 		 Adapter->HWAddress[3] & 0xff,
994 		 Adapter->HWAddress[4] & 0xff,
995 		 Adapter->HWAddress[5] & 0xff));
996 
997     GetDataPtr( NdisPacket, 0, &OldData, &OldSize );
998 
999     NdisStatus = AllocatePacketWithBuffer(&XmitPacket, NULL, OldSize + Adapter->HeaderSize);
1000     if (NdisStatus != NDIS_STATUS_SUCCESS) {
1001         (*PC(NdisPacket)->DLComplete)(PC(NdisPacket)->Context, NdisPacket, NDIS_STATUS_RESOURCES);
1002         return;
1003     }
1004 
1005     GetDataPtr(XmitPacket, 0, &Data, &Size);
1006 
1007     RtlCopyMemory(Data + Adapter->HeaderSize, OldData, OldSize);
1008 
1009     (*PC(NdisPacket)->DLComplete)(PC(NdisPacket)->Context, NdisPacket, NDIS_STATUS_SUCCESS);
1010 
1011     switch (Adapter->Media) {
1012         case NdisMedium802_3:
1013             EHeader = (PETH_HEADER)Data;
1014 
1015             if (LinkAddress) {
1016                 /* Unicast address */
1017                 RtlCopyMemory(EHeader->DstAddr, LinkAddress, IEEE_802_ADDR_LENGTH);
1018             } else {
1019                 /* Broadcast address */
1020                 RtlFillMemory(EHeader->DstAddr, IEEE_802_ADDR_LENGTH, 0xFF);
1021             }
1022 
1023             RtlCopyMemory(EHeader->SrcAddr, Adapter->HWAddress, IEEE_802_ADDR_LENGTH);
1024 
1025             switch (Type) {
1026                 case LAN_PROTO_IPv4:
1027                     EHeader->EType = ETYPE_IPv4;
1028                     break;
1029                 case LAN_PROTO_ARP:
1030                     EHeader->EType = ETYPE_ARP;
1031                     break;
1032                 case LAN_PROTO_IPv6:
1033                     EHeader->EType = ETYPE_IPv6;
1034                     break;
1035                 default:
1036                     ASSERT(FALSE);
1037                     return;
1038             }
1039             break;
1040 
1041         default:
1042             /* FIXME: Support other medias */
1043             break;
1044     }
1045 
1046 	TI_DbgPrint( MID_TRACE, ("LinkAddress: %x\n", LinkAddress));
1047 	if( LinkAddress ) {
1048 	    TI_DbgPrint
1049 		( MID_TRACE,
1050 		  ("Link Address [%02x %02x %02x %02x %02x %02x]\n",
1051 		   ((PCHAR)LinkAddress)[0] & 0xff,
1052 		   ((PCHAR)LinkAddress)[1] & 0xff,
1053 		   ((PCHAR)LinkAddress)[2] & 0xff,
1054 		   ((PCHAR)LinkAddress)[3] & 0xff,
1055 		   ((PCHAR)LinkAddress)[4] & 0xff,
1056 		   ((PCHAR)LinkAddress)[5] & 0xff));
1057 	}
1058 
1059     if (Adapter->MTU < Size) {
1060         /* This is NOT a pointer. MSDN explicitly says so. */
1061         NDIS_PER_PACKET_INFO_FROM_PACKET(NdisPacket,
1062                                          TcpLargeSendPacketInfo) = (PVOID)((ULONG_PTR)Adapter->MTU);
1063     }
1064 
1065     /* Update interface stats */
1066     Interface->Stats.OutBytes += Size;
1067 
1068 	TcpipAcquireSpinLock( &Adapter->Lock, &OldIrql );
1069 	TI_DbgPrint(MID_TRACE, ("NdisSend\n"));
1070 	NdisSend(&NdisStatus, Adapter->NdisHandle, XmitPacket);
1071 	TI_DbgPrint(MID_TRACE, ("NdisSend %s\n",
1072 				NdisStatus == NDIS_STATUS_PENDING ?
1073 				"Pending" : "Complete"));
1074 	TcpipReleaseSpinLock( &Adapter->Lock, OldIrql );
1075 
1076 	/* I had a talk with vizzini: these really ought to be here.
1077 	 * we're supposed to see these completed by ndis *only* when
1078 	 * status_pending is returned.  Note that this is different from
1079 	 * the situation with IRPs. */
1080         if (NdisStatus != NDIS_STATUS_PENDING)
1081             ProtocolSendComplete((NDIS_HANDLE)Context, XmitPacket, NdisStatus);
1082 }
1083 
1084 static NTSTATUS
1085 OpenRegistryKey( PNDIS_STRING RegistryPath, PHANDLE RegHandle ) {
1086     OBJECT_ATTRIBUTES Attributes;
1087     NTSTATUS Status;
1088 
1089     InitializeObjectAttributes(&Attributes, RegistryPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
1090     Status = ZwOpenKey(RegHandle, KEY_ALL_ACCESS, &Attributes);
1091     return Status;
1092 }
1093 
1094 static NTSTATUS ReadStringFromRegistry( HANDLE RegHandle,
1095 					PWCHAR RegistryValue,
1096 					PUNICODE_STRING String ) {
1097     UNICODE_STRING ValueName;
1098     UNICODE_STRING UnicodeString;
1099     NTSTATUS Status;
1100     ULONG ResultLength;
1101     UCHAR buf[1024];
1102     PKEY_VALUE_PARTIAL_INFORMATION Information = (PKEY_VALUE_PARTIAL_INFORMATION)buf;
1103 
1104     RtlInitUnicodeString(&ValueName, RegistryValue);
1105     Status =
1106 	ZwQueryValueKey(RegHandle,
1107 			&ValueName,
1108 			KeyValuePartialInformation,
1109 			Information,
1110 			sizeof(buf),
1111 			&ResultLength);
1112 
1113     if (!NT_SUCCESS(Status))
1114 	return Status;
1115     /* IP address is stored as a REG_MULTI_SZ - we only pay attention to the first one though */
1116     TI_DbgPrint(MIN_TRACE, ("Information DataLength: 0x%x\n", Information->DataLength));
1117 
1118     UnicodeString.Buffer = (PWCHAR)&Information->Data;
1119     UnicodeString.Length = Information->DataLength - sizeof(WCHAR);
1120     UnicodeString.MaximumLength = Information->DataLength;
1121 
1122     String->Buffer =
1123 	(PWCHAR)ExAllocatePoolWithTag( NonPagedPool,
1124 				UnicodeString.MaximumLength + sizeof(WCHAR), REG_STR_TAG );
1125 
1126     if( !String->Buffer ) return STATUS_NO_MEMORY;
1127 
1128     String->MaximumLength = UnicodeString.MaximumLength;
1129     RtlCopyUnicodeString( String, &UnicodeString );
1130 
1131     return STATUS_SUCCESS;
1132 }
1133 
1134 /*
1135  * Utility to copy and append two unicode strings.
1136  *
1137  * IN OUT PUNICODE_STRING ResultFirst -> First string and result
1138  * IN     PUNICODE_STRING Second      -> Second string to append
1139  * IN     BOOL            Deallocate  -> TRUE: Deallocate First string before
1140  *                                       overwriting.
1141  *
1142  * Returns NTSTATUS.
1143  */
1144 
1145 NTSTATUS NTAPI AppendUnicodeString(PUNICODE_STRING ResultFirst,
1146 				   PUNICODE_STRING Second,
1147 				   BOOLEAN Deallocate) {
1148     NTSTATUS Status;
1149     UNICODE_STRING Ustr = *ResultFirst;
1150     PWSTR new_string = ExAllocatePoolWithTag
1151         (PagedPool,
1152          (ResultFirst->Length + Second->Length + sizeof(WCHAR)), TEMP_STRING_TAG);
1153     if( !new_string ) {
1154 	return STATUS_NO_MEMORY;
1155     }
1156     memcpy( new_string, ResultFirst->Buffer, ResultFirst->Length );
1157     memcpy( new_string + ResultFirst->Length / sizeof(WCHAR),
1158 	    Second->Buffer, Second->Length );
1159     if( Deallocate ) RtlFreeUnicodeString(ResultFirst);
1160     ResultFirst->Length = Ustr.Length + Second->Length;
1161     ResultFirst->MaximumLength = ResultFirst->Length;
1162     new_string[ResultFirst->Length / sizeof(WCHAR)] = 0;
1163     Status = RtlCreateUnicodeString(ResultFirst,new_string) ?
1164 	STATUS_SUCCESS : STATUS_NO_MEMORY;
1165     ExFreePoolWithTag(new_string, TEMP_STRING_TAG);
1166     return Status;
1167 }
1168 
1169 static NTSTATUS CheckForDeviceDesc( PUNICODE_STRING EnumKeyName,
1170                                     PUNICODE_STRING TargetKeyName,
1171                                     PUNICODE_STRING Name,
1172                                     PUNICODE_STRING DeviceDesc ) {
1173     UNICODE_STRING RootDevice = { 0, 0, NULL }, LinkageKeyName = { 0, 0, NULL };
1174     UNICODE_STRING DescKeyName = { 0, 0, NULL }, Linkage = { 0, 0, NULL };
1175     UNICODE_STRING BackSlash = { 0, 0, NULL };
1176     HANDLE DescKey = NULL, LinkageKey = NULL;
1177     NTSTATUS Status;
1178 
1179     TI_DbgPrint(DEBUG_DATALINK,("EnumKeyName %wZ\n", EnumKeyName));
1180 
1181     RtlInitUnicodeString(&BackSlash, L"\\");
1182     RtlInitUnicodeString(&Linkage, L"\\Linkage");
1183 
1184     RtlInitUnicodeString(&DescKeyName, L"");
1185     AppendUnicodeString( &DescKeyName, EnumKeyName, FALSE );
1186     AppendUnicodeString( &DescKeyName, &BackSlash, TRUE );
1187     AppendUnicodeString( &DescKeyName, TargetKeyName, TRUE );
1188 
1189     RtlInitUnicodeString(&LinkageKeyName, L"");
1190     AppendUnicodeString( &LinkageKeyName, &DescKeyName, FALSE );
1191     AppendUnicodeString( &LinkageKeyName, &Linkage, TRUE );
1192 
1193     Status = OpenRegistryKey( &LinkageKeyName, &LinkageKey );
1194     if( !NT_SUCCESS(Status) ) goto cleanup;
1195 
1196     Status = ReadStringFromRegistry( LinkageKey, L"RootDevice", &RootDevice );
1197     if( !NT_SUCCESS(Status) ) goto cleanup;
1198 
1199     if( RtlCompareUnicodeString( &RootDevice, Name, TRUE ) == 0 ) {
1200         Status = OpenRegistryKey( &DescKeyName, &DescKey );
1201         if( !NT_SUCCESS(Status) ) goto cleanup;
1202 
1203         Status = ReadStringFromRegistry( DescKey, L"DriverDesc", DeviceDesc );
1204         if( !NT_SUCCESS(Status) ) goto cleanup;
1205 
1206         TI_DbgPrint(DEBUG_DATALINK,("ADAPTER DESC: %wZ\n", DeviceDesc));
1207     } else Status = STATUS_UNSUCCESSFUL;
1208 
1209 cleanup:
1210     RtlFreeUnicodeString( &RootDevice );
1211     RtlFreeUnicodeString( &LinkageKeyName );
1212     RtlFreeUnicodeString( &DescKeyName );
1213     if( LinkageKey ) ZwClose( LinkageKey );
1214     if( DescKey ) ZwClose( DescKey );
1215 
1216     TI_DbgPrint(DEBUG_DATALINK,("Returning %x\n", Status));
1217 
1218     return Status;
1219 }
1220 
1221 static NTSTATUS FindDeviceDescForAdapter( PUNICODE_STRING Name,
1222                                           PUNICODE_STRING DeviceDesc ) {
1223     UNICODE_STRING EnumKeyName, TargetKeyName;
1224     HANDLE EnumKey;
1225     NTSTATUS Status;
1226     ULONG i;
1227     KEY_BASIC_INFORMATION *Kbio =
1228         ExAllocatePoolWithTag(NonPagedPool, sizeof(KEY_BASIC_INFORMATION), KBIO_TAG);
1229     ULONG KbioLength = sizeof(KEY_BASIC_INFORMATION), ResultLength;
1230 
1231     RtlInitUnicodeString( DeviceDesc, NULL );
1232 
1233     if( !Kbio ) return STATUS_INSUFFICIENT_RESOURCES;
1234 
1235     RtlInitUnicodeString
1236         (&EnumKeyName, CCS_ROOT L"\\Control\\Class\\" TCPIP_GUID);
1237 
1238     Status = OpenRegistryKey( &EnumKeyName, &EnumKey );
1239 
1240     if( !NT_SUCCESS(Status) ) {
1241         TI_DbgPrint(DEBUG_DATALINK,("Couldn't open Enum key %wZ: %x\n",
1242                                     &EnumKeyName, Status));
1243         ExFreePoolWithTag( Kbio, KBIO_TAG );
1244         return Status;
1245     }
1246 
1247     for( i = 0; NT_SUCCESS(Status); i++ ) {
1248         Status = ZwEnumerateKey( EnumKey, i, KeyBasicInformation,
1249                                  Kbio, KbioLength, &ResultLength );
1250 
1251         if( Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW ) {
1252             ExFreePoolWithTag( Kbio, KBIO_TAG );
1253             KbioLength = ResultLength;
1254             Kbio = ExAllocatePoolWithTag( NonPagedPool, KbioLength, KBIO_TAG );
1255             if( !Kbio ) {
1256                 TI_DbgPrint(DEBUG_DATALINK,("Failed to allocate memory\n"));
1257                 ZwClose( EnumKey );
1258                 return STATUS_NO_MEMORY;
1259             }
1260 
1261             Status = ZwEnumerateKey( EnumKey, i, KeyBasicInformation,
1262                                      Kbio, KbioLength, &ResultLength );
1263 
1264             if( !NT_SUCCESS(Status) ) {
1265                 TI_DbgPrint(DEBUG_DATALINK,("Couldn't enum key child %d\n", i));
1266                 ZwClose( EnumKey );
1267                 ExFreePoolWithTag( Kbio, KBIO_TAG );
1268                 return Status;
1269             }
1270         }
1271 
1272         if( NT_SUCCESS(Status) ) {
1273             TargetKeyName.Length = TargetKeyName.MaximumLength =
1274                 Kbio->NameLength;
1275             TargetKeyName.Buffer = Kbio->Name;
1276 
1277             Status = CheckForDeviceDesc
1278                 ( &EnumKeyName, &TargetKeyName, Name, DeviceDesc );
1279             if( NT_SUCCESS(Status) ) {
1280                 ZwClose( EnumKey );
1281                 ExFreePoolWithTag( Kbio, KBIO_TAG );
1282                 return Status;
1283             } else Status = STATUS_SUCCESS;
1284         }
1285     }
1286 
1287     ZwClose( EnumKey );
1288     ExFreePoolWithTag( Kbio, KBIO_TAG );
1289     return STATUS_UNSUCCESSFUL;
1290 }
1291 
1292 VOID GetName( PUNICODE_STRING RegistryKey,
1293               PUNICODE_STRING OutName ) {
1294     PWCHAR Ptr;
1295     UNICODE_STRING PartialRegistryKey;
1296 
1297     PartialRegistryKey.Buffer =
1298         RegistryKey->Buffer + wcslen(CCS_ROOT L"\\Services\\");
1299     Ptr = PartialRegistryKey.Buffer;
1300 
1301     while( *Ptr != L'\\' &&
1302            ((PCHAR)Ptr) < ((PCHAR)RegistryKey->Buffer) + RegistryKey->Length )
1303         Ptr++;
1304 
1305     PartialRegistryKey.Length = PartialRegistryKey.MaximumLength =
1306         (Ptr - PartialRegistryKey.Buffer) * sizeof(WCHAR);
1307 
1308     RtlInitUnicodeString( OutName, L"" );
1309     AppendUnicodeString( OutName, &PartialRegistryKey, FALSE );
1310 }
1311 
1312 BOOLEAN BindAdapter(
1313     PLAN_ADAPTER Adapter,
1314     PNDIS_STRING RegistryPath)
1315 /*
1316  * FUNCTION: Binds a LAN adapter to IP layer
1317  * ARGUMENTS:
1318  *     Adapter = Pointer to LAN_ADAPTER structure
1319  * NOTES:
1320  *    We set the lookahead buffer size, set the packet filter and
1321  *    bind the adapter to IP layer
1322  */
1323 {
1324     PIP_INTERFACE IF;
1325     NDIS_STATUS NdisStatus;
1326     LLIP_BIND_INFO BindInfo;
1327     ULONG Lookahead = LOOKAHEAD_SIZE;
1328     NTSTATUS Status;
1329     NDIS_MEDIA_STATE MediaState;
1330 
1331     TI_DbgPrint(DEBUG_DATALINK, ("Called.\n"));
1332 
1333     Adapter->State = LAN_STATE_OPENING;
1334 
1335     NdisStatus = NDISCall(Adapter,
1336                           NdisRequestSetInformation,
1337                           OID_GEN_CURRENT_LOOKAHEAD,
1338                           &Lookahead,
1339                           sizeof(ULONG));
1340     if (NdisStatus != NDIS_STATUS_SUCCESS) {
1341         TI_DbgPrint(DEBUG_DATALINK, ("Could not set lookahead buffer size (0x%X).\n", NdisStatus));
1342         return FALSE;
1343     }
1344 
1345     /* Bind the adapter to IP layer */
1346     BindInfo.Context       = Adapter;
1347     BindInfo.HeaderSize    = Adapter->HeaderSize;
1348     BindInfo.MinFrameSize  = Adapter->MinFrameSize;
1349     BindInfo.Address       = (PUCHAR)&Adapter->HWAddress;
1350     BindInfo.AddressLength = Adapter->HWAddressLength;
1351     BindInfo.Transmit      = LANTransmit;
1352 
1353     IF = IPCreateInterface(&BindInfo);
1354 
1355     if (!IF) {
1356         TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
1357         return FALSE;
1358     }
1359 
1360     /*
1361      * Query per-adapter configuration from the registry
1362      * In case anyone is curious:  there *is* an Ndis configuration api
1363      * for this sort of thing, but it doesn't really support things like
1364      * REG_MULTI_SZ very well, and there is a note in the DDK that says that
1365      * protocol drivers developed for win2k and above just use the native
1366      * services (ZwOpenKey, etc).
1367      */
1368 
1369     GetName( RegistryPath, &IF->Name );
1370 
1371     Status = FindDeviceDescForAdapter( &IF->Name, &IF->Description );
1372     if (!NT_SUCCESS(Status)) {
1373         TI_DbgPrint(MIN_TRACE, ("Failed to get device description.\n"));
1374         IPDestroyInterface(IF);
1375         return FALSE;
1376     }
1377 
1378     TI_DbgPrint(DEBUG_DATALINK,("Adapter Description: %wZ\n",
1379                 &IF->Description));
1380 
1381     /* Get maximum link speed */
1382     NdisStatus = NDISCall(Adapter,
1383                           NdisRequestQueryInformation,
1384                           OID_GEN_LINK_SPEED,
1385                           &IF->Speed,
1386                           sizeof(UINT));
1387 
1388     if (!NT_SUCCESS(NdisStatus))
1389         IF->Speed = IP_DEFAULT_LINK_SPEED;
1390 
1391     Adapter->Speed = IF->Speed * 100L;
1392 
1393     /* Get maximum frame size */
1394     NdisStatus = NDISCall(Adapter,
1395                           NdisRequestQueryInformation,
1396                           OID_GEN_MAXIMUM_FRAME_SIZE,
1397                           &Adapter->MTU,
1398                           sizeof(UINT));
1399     if (NdisStatus != NDIS_STATUS_SUCCESS)
1400         return FALSE;
1401 
1402     IF->MTU = Adapter->MTU;
1403 
1404     /* Get maximum packet size */
1405     NdisStatus = NDISCall(Adapter,
1406                           NdisRequestQueryInformation,
1407                           OID_GEN_MAXIMUM_TOTAL_SIZE,
1408                           &Adapter->MaxPacketSize,
1409                           sizeof(UINT));
1410     if (NdisStatus != NDIS_STATUS_SUCCESS)
1411         return FALSE;
1412 
1413     /* Register interface with IP layer */
1414     IPRegisterInterface(IF);
1415 
1416     /* Store adapter context */
1417     Adapter->Context = IF;
1418 
1419     /* Get the media state */
1420     NdisStatus = NDISCall(Adapter,
1421                           NdisRequestQueryInformation,
1422                           OID_GEN_MEDIA_CONNECT_STATUS,
1423                           &MediaState,
1424                           sizeof(MediaState));
1425     if (NdisStatus != NDIS_STATUS_SUCCESS) {
1426         TI_DbgPrint(DEBUG_DATALINK, ("Could not query media status (0x%X).\n", NdisStatus));
1427         IPUnregisterInterface(IF);
1428         IPDestroyInterface(IF);
1429         return FALSE;
1430     }
1431 
1432     /* Indicate the current media state */
1433     ProtocolStatus(Adapter,
1434                    (MediaState == NdisMediaStateConnected) ? NDIS_STATUS_MEDIA_CONNECT : NDIS_STATUS_MEDIA_DISCONNECT,
1435                    NULL, 0);
1436 
1437     /* Set packet filter so we can send and receive packets */
1438     NdisStatus = NDISCall(Adapter,
1439                           NdisRequestSetInformation,
1440                           OID_GEN_CURRENT_PACKET_FILTER,
1441                           &Adapter->PacketFilter,
1442                           sizeof(UINT));
1443 
1444     if (NdisStatus != NDIS_STATUS_SUCCESS) {
1445         TI_DbgPrint(DEBUG_DATALINK, ("Could not set packet filter (0x%X).\n", NdisStatus));
1446         IPUnregisterInterface(IF);
1447         IPDestroyInterface(IF);
1448         return FALSE;
1449     }
1450 
1451     return TRUE;
1452 }
1453 
1454 
1455 VOID UnbindAdapter(
1456     PLAN_ADAPTER Adapter)
1457 /*
1458  * FUNCTION: Unbinds a LAN adapter from IP layer
1459  * ARGUMENTS:
1460  *     Adapter = Pointer to LAN_ADAPTER structure
1461  */
1462 {
1463     TI_DbgPrint(DEBUG_DATALINK, ("Called.\n"));
1464 
1465     if (Adapter->State == LAN_STATE_STARTED) {
1466         PIP_INTERFACE IF = Adapter->Context;
1467 
1468         IPUnregisterInterface(IF);
1469 
1470         IPDestroyInterface(IF);
1471     }
1472 }
1473 
1474 
1475 NDIS_STATUS LANRegisterAdapter(
1476     PNDIS_STRING AdapterName,
1477     PNDIS_STRING RegistryPath)
1478 /*
1479  * FUNCTION: Registers protocol with an NDIS adapter
1480  * ARGUMENTS:
1481  *     AdapterName = Pointer to string with name of adapter to register
1482  *     Adapter     = Address of pointer to a LAN_ADAPTER structure
1483  * RETURNS:
1484  *     Status of operation
1485  */
1486 {
1487     PLAN_ADAPTER IF;
1488     NDIS_STATUS NdisStatus;
1489     NDIS_STATUS OpenStatus;
1490     UINT MediaIndex;
1491     NDIS_MEDIUM MediaArray[MAX_MEDIA];
1492     UINT AddressOID;
1493 
1494     TI_DbgPrint(DEBUG_DATALINK, ("Called.\n"));
1495 
1496     IF = ExAllocatePoolWithTag(NonPagedPool, sizeof(LAN_ADAPTER), LAN_ADAPTER_TAG);
1497     if (!IF) {
1498         TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
1499         return NDIS_STATUS_RESOURCES;
1500     }
1501 
1502     RtlZeroMemory(IF, sizeof(LAN_ADAPTER));
1503 
1504     /* Put adapter in stopped state */
1505     IF->State = LAN_STATE_STOPPED;
1506 
1507     /* Initialize protecting spin lock */
1508     KeInitializeSpinLock(&IF->Lock);
1509 
1510     KeInitializeEvent(&IF->Event, SynchronizationEvent, FALSE);
1511 
1512     /* Initialize array with media IDs we support */
1513     MediaArray[MEDIA_ETH] = NdisMedium802_3;
1514 
1515     TI_DbgPrint(DEBUG_DATALINK,("opening adapter %wZ\n", AdapterName));
1516     /* Open the adapter. */
1517     NdisOpenAdapter(&NdisStatus,
1518                     &OpenStatus,
1519                     &IF->NdisHandle,
1520                     &MediaIndex,
1521                     MediaArray,
1522                     MAX_MEDIA,
1523                     NdisProtocolHandle,
1524                     IF,
1525                     AdapterName,
1526                     0,
1527                     NULL);
1528 
1529     /* Wait until the adapter is opened */
1530     if (NdisStatus == NDIS_STATUS_PENDING)
1531         KeWaitForSingleObject(&IF->Event, UserRequest, KernelMode, FALSE, NULL);
1532     else if (NdisStatus != NDIS_STATUS_SUCCESS) {
1533 	TI_DbgPrint(DEBUG_DATALINK,("denying adapter %wZ\n", AdapterName));
1534 	ExFreePoolWithTag(IF, LAN_ADAPTER_TAG);
1535         return NdisStatus;
1536     }
1537 
1538     IF->Media = MediaArray[MediaIndex];
1539 
1540     /* Fill LAN_ADAPTER structure with some adapter specific information */
1541     switch (IF->Media) {
1542     case NdisMedium802_3:
1543         IF->HWAddressLength = IEEE_802_ADDR_LENGTH;
1544         IF->BCastMask       = BCAST_ETH_MASK;
1545         IF->BCastCheck      = BCAST_ETH_CHECK;
1546         IF->BCastOffset     = BCAST_ETH_OFFSET;
1547         IF->HeaderSize      = sizeof(ETH_HEADER);
1548         IF->MinFrameSize    = 60;
1549         AddressOID          = OID_802_3_CURRENT_ADDRESS;
1550         IF->PacketFilter    =
1551             NDIS_PACKET_TYPE_BROADCAST |
1552             NDIS_PACKET_TYPE_DIRECTED  |
1553             NDIS_PACKET_TYPE_MULTICAST;
1554         break;
1555 
1556     default:
1557         /* Unsupported media */
1558         TI_DbgPrint(MIN_TRACE, ("Unsupported media.\n"));
1559         ExFreePoolWithTag(IF, LAN_ADAPTER_TAG);
1560         return NDIS_STATUS_NOT_SUPPORTED;
1561     }
1562 
1563     /* Get maximum number of packets we can pass to NdisSend(Packets) at one time */
1564     NdisStatus = NDISCall(IF,
1565                           NdisRequestQueryInformation,
1566                           OID_GEN_MAXIMUM_SEND_PACKETS,
1567                           &IF->MaxSendPackets,
1568                           sizeof(UINT));
1569     if (NdisStatus != NDIS_STATUS_SUCCESS)
1570         /* Legacy NIC drivers may not support this query, if it fails we
1571            assume it can send at least one packet per call to NdisSend(Packets) */
1572         IF->MaxSendPackets = 1;
1573 
1574     /* Get current hardware address */
1575     NdisStatus = NDISCall(IF,
1576                           NdisRequestQueryInformation,
1577                           AddressOID,
1578                           &IF->HWAddress,
1579                           IF->HWAddressLength);
1580     if (NdisStatus != NDIS_STATUS_SUCCESS) {
1581         TI_DbgPrint(MIN_TRACE, ("Query for current hardware address failed.\n"));
1582         ExFreePoolWithTag(IF, LAN_ADAPTER_TAG);
1583         return NdisStatus;
1584     }
1585 
1586     /* Bind adapter to IP layer */
1587     if( !BindAdapter(IF, RegistryPath) ) {
1588 	TI_DbgPrint(DEBUG_DATALINK,("denying adapter %wZ (BindAdapter)\n", AdapterName));
1589 	ExFreePoolWithTag(IF, LAN_ADAPTER_TAG);
1590 	return NDIS_STATUS_NOT_ACCEPTED;
1591     }
1592 
1593     /* Add adapter to the adapter list */
1594     ExInterlockedInsertTailList(&AdapterListHead,
1595                                 &IF->ListEntry,
1596                                 &AdapterListLock);
1597 
1598     TI_DbgPrint(DEBUG_DATALINK, ("Leaving.\n"));
1599 
1600     return NDIS_STATUS_SUCCESS;
1601 }
1602 
1603 
1604 NDIS_STATUS LANUnregisterAdapter(
1605     PLAN_ADAPTER Adapter)
1606 /*
1607  * FUNCTION: Unregisters protocol with NDIS adapter
1608  * ARGUMENTS:
1609  *     Adapter = Pointer to a LAN_ADAPTER structure
1610  * RETURNS:
1611  *     Status of operation
1612  */
1613 {
1614     KIRQL OldIrql;
1615     NDIS_HANDLE NdisHandle;
1616     NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS;
1617 
1618     TI_DbgPrint(DEBUG_DATALINK, ("Called.\n"));
1619 
1620     /* Unlink the adapter from the list */
1621     RemoveEntryList(&Adapter->ListEntry);
1622 
1623     /* Unbind adapter from IP layer */
1624     UnbindAdapter(Adapter);
1625 
1626     TcpipAcquireSpinLock(&Adapter->Lock, &OldIrql);
1627     NdisHandle = Adapter->NdisHandle;
1628     if (NdisHandle) {
1629         Adapter->NdisHandle = NULL;
1630         TcpipReleaseSpinLock(&Adapter->Lock, OldIrql);
1631 
1632         NdisCloseAdapter(&NdisStatus, NdisHandle);
1633         if (NdisStatus == NDIS_STATUS_PENDING) {
1634             TcpipWaitForSingleObject(&Adapter->Event,
1635                                      UserRequest,
1636                                      KernelMode,
1637                                      FALSE,
1638                                      NULL);
1639             NdisStatus = Adapter->NdisStatus;
1640         }
1641     } else
1642         TcpipReleaseSpinLock(&Adapter->Lock, OldIrql);
1643 
1644     FreeAdapter(Adapter);
1645 
1646     return NdisStatus;
1647 }
1648 
1649 VOID
1650 NTAPI
1651 LANUnregisterProtocol(VOID)
1652 /*
1653  * FUNCTION: Unregisters this protocol driver with NDIS
1654  * NOTES: Does not care wether we are already registered
1655  */
1656 {
1657     TI_DbgPrint(DEBUG_DATALINK, ("Called.\n"));
1658 
1659     if (ProtocolRegistered) {
1660         NDIS_STATUS NdisStatus;
1661         PLIST_ENTRY CurrentEntry;
1662         PLIST_ENTRY NextEntry;
1663         PLAN_ADAPTER Current;
1664         KIRQL OldIrql;
1665 
1666         TcpipAcquireSpinLock(&AdapterListLock, &OldIrql);
1667 
1668         /* Search the list and remove every adapter we find */
1669         CurrentEntry = AdapterListHead.Flink;
1670         while (CurrentEntry != &AdapterListHead) {
1671             NextEntry = CurrentEntry->Flink;
1672             Current = CONTAINING_RECORD(CurrentEntry, LAN_ADAPTER, ListEntry);
1673             /* Unregister it */
1674             LANUnregisterAdapter(Current);
1675             CurrentEntry = NextEntry;
1676         }
1677 
1678         TcpipReleaseSpinLock(&AdapterListLock, OldIrql);
1679 
1680         NdisDeregisterProtocol(&NdisStatus, NdisProtocolHandle);
1681         ProtocolRegistered = FALSE;
1682     }
1683 }
1684 
1685 VOID
1686 NTAPI
1687 ProtocolUnbindAdapter(
1688     PNDIS_STATUS Status,
1689     NDIS_HANDLE ProtocolBindingContext,
1690     NDIS_HANDLE UnbindContext)
1691 {
1692     /* We don't pend any unbinding so we can just ignore UnbindContext */
1693     *Status = LANUnregisterAdapter((PLAN_ADAPTER)ProtocolBindingContext);
1694 }
1695 
1696 NTSTATUS LANRegisterProtocol(
1697     PNDIS_STRING Name)
1698 /*
1699  * FUNCTION: Registers this protocol driver with NDIS
1700  * ARGUMENTS:
1701  *     Name = Name of this protocol driver
1702  * RETURNS:
1703  *     Status of operation
1704  */
1705 {
1706     NDIS_STATUS NdisStatus;
1707     NDIS_PROTOCOL_CHARACTERISTICS ProtChars;
1708 
1709     TI_DbgPrint(DEBUG_DATALINK, ("Called.\n"));
1710 
1711     InitializeListHead(&AdapterListHead);
1712     KeInitializeSpinLock(&AdapterListLock);
1713 
1714     /* Set up protocol characteristics */
1715     RtlZeroMemory(&ProtChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
1716     ProtChars.MajorNdisVersion               = NDIS_VERSION_MAJOR;
1717     ProtChars.MinorNdisVersion               = NDIS_VERSION_MINOR;
1718     ProtChars.Name.Length                    = Name->Length;
1719     ProtChars.Name.Buffer                    = Name->Buffer;
1720     ProtChars.Name.MaximumLength             = Name->MaximumLength;
1721     ProtChars.OpenAdapterCompleteHandler     = ProtocolOpenAdapterComplete;
1722     ProtChars.CloseAdapterCompleteHandler    = ProtocolCloseAdapterComplete;
1723     ProtChars.ResetCompleteHandler           = ProtocolResetComplete;
1724     ProtChars.RequestCompleteHandler         = ProtocolRequestComplete;
1725     ProtChars.SendCompleteHandler            = ProtocolSendComplete;
1726     ProtChars.TransferDataCompleteHandler    = ProtocolTransferDataComplete;
1727     ProtChars.ReceivePacketHandler           = ProtocolReceivePacket;
1728     ProtChars.ReceiveHandler                 = ProtocolReceive;
1729     ProtChars.ReceiveCompleteHandler         = ProtocolReceiveComplete;
1730     ProtChars.StatusHandler                  = ProtocolStatus;
1731     ProtChars.StatusCompleteHandler          = ProtocolStatusComplete;
1732     ProtChars.BindAdapterHandler             = ProtocolBindAdapter;
1733     ProtChars.PnPEventHandler                = ProtocolPnPEvent;
1734     ProtChars.UnbindAdapterHandler           = ProtocolUnbindAdapter;
1735     ProtChars.UnloadHandler                  = LANUnregisterProtocol;
1736 
1737     /* Try to register protocol */
1738     NdisRegisterProtocol(&NdisStatus,
1739                          &NdisProtocolHandle,
1740                          &ProtChars,
1741                          sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
1742     if (NdisStatus != NDIS_STATUS_SUCCESS)
1743     {
1744         TI_DbgPrint(DEBUG_DATALINK, ("NdisRegisterProtocol failed, status 0x%x\n", NdisStatus));
1745         return (NTSTATUS)NdisStatus;
1746     }
1747 
1748     ProtocolRegistered = TRUE;
1749 
1750     return STATUS_SUCCESS;
1751 }
1752 
1753 /* EOF */
1754