Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenlan.c
Go to the documentation of this file.
00001 /* 00002 * COPYRIGHT: See COPYING in the top level directory 00003 * PROJECT: ReactOS TCP/IP protocol driver 00004 * FILE: datalink/lan.c 00005 * PURPOSE: Local Area Network media routines 00006 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) 00007 * REVISIONS: 00008 * CSH 01/08-2000 Created 00009 */ 00010 00011 #include "precomp.h" 00012 00013 UINT TransferDataCalled = 0; 00014 UINT TransferDataCompleteCalled = 0; 00015 00016 #define CCS_ROOT L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet" 00017 #define TCPIP_GUID L"{4D36E972-E325-11CE-BFC1-08002BE10318}" 00018 00019 typedef struct _LAN_WQ_ITEM { 00020 LIST_ENTRY ListEntry; 00021 PNDIS_PACKET Packet; 00022 PLAN_ADAPTER Adapter; 00023 UINT BytesTransferred; 00024 BOOLEAN LegacyReceive; 00025 } LAN_WQ_ITEM, *PLAN_WQ_ITEM; 00026 00027 typedef struct _RECONFIGURE_CONTEXT { 00028 ULONG State; 00029 PLAN_ADAPTER Adapter; 00030 } RECONFIGURE_CONTEXT, *PRECONFIGURE_CONTEXT; 00031 00032 NDIS_HANDLE NdisProtocolHandle = (NDIS_HANDLE)NULL; 00033 BOOLEAN ProtocolRegistered = FALSE; 00034 LIST_ENTRY AdapterListHead; 00035 KSPIN_LOCK AdapterListLock; 00036 00037 NDIS_STATUS NDISCall( 00038 PLAN_ADAPTER Adapter, 00039 NDIS_REQUEST_TYPE Type, 00040 NDIS_OID OID, 00041 PVOID Buffer, 00042 UINT Length) 00043 /* 00044 * FUNCTION: Send a request to NDIS 00045 * ARGUMENTS: 00046 * Adapter = Pointer to a LAN_ADAPTER structure 00047 * Type = Type of request (Set or Query) 00048 * OID = Value to be set/queried for 00049 * Buffer = Pointer to a buffer to use 00050 * Length = Number of bytes in Buffer 00051 * RETURNS: 00052 * Status of operation 00053 */ 00054 { 00055 NDIS_REQUEST Request; 00056 NDIS_STATUS NdisStatus; 00057 00058 Request.RequestType = Type; 00059 if (Type == NdisRequestSetInformation) { 00060 Request.DATA.SET_INFORMATION.Oid = OID; 00061 Request.DATA.SET_INFORMATION.InformationBuffer = Buffer; 00062 Request.DATA.SET_INFORMATION.InformationBufferLength = Length; 00063 } else { 00064 Request.DATA.QUERY_INFORMATION.Oid = OID; 00065 Request.DATA.QUERY_INFORMATION.InformationBuffer = Buffer; 00066 Request.DATA.QUERY_INFORMATION.InformationBufferLength = Length; 00067 } 00068 00069 if (Adapter->State != LAN_STATE_RESETTING) { 00070 NdisRequest(&NdisStatus, Adapter->NdisHandle, &Request); 00071 } else { 00072 NdisStatus = NDIS_STATUS_NOT_ACCEPTED; 00073 } 00074 00075 /* Wait for NDIS to complete the request */ 00076 if (NdisStatus == NDIS_STATUS_PENDING) { 00077 KeWaitForSingleObject(&Adapter->Event, 00078 UserRequest, 00079 KernelMode, 00080 FALSE, 00081 NULL); 00082 NdisStatus = Adapter->NdisStatus; 00083 } 00084 00085 return NdisStatus; 00086 } 00087 00088 /* Used by legacy ProtocolReceive for packet type */ 00089 NDIS_STATUS 00090 GetPacketTypeFromHeaderBuffer(PLAN_ADAPTER Adapter, 00091 PVOID HeaderBuffer, 00092 ULONG HeaderBufferSize, 00093 PULONG PacketType) 00094 { 00095 PETH_HEADER EthHeader = HeaderBuffer; 00096 00097 if (HeaderBufferSize < Adapter->HeaderSize) 00098 { 00099 TI_DbgPrint(DEBUG_DATALINK, ("Runt frame (size %d).\n", HeaderBufferSize)); 00100 return NDIS_STATUS_NOT_ACCEPTED; 00101 } 00102 00103 switch (Adapter->Media) 00104 { 00105 case NdisMedium802_3: 00106 /* Ethernet and IEEE 802.3 frames can be destinguished by 00107 looking at the IEEE 802.3 length field. This field is 00108 less than or equal to 1500 for a valid IEEE 802.3 frame 00109 and larger than 1500 is it's a valid EtherType value. 00110 See RFC 1122, section 2.3.3 for more information */ 00111 00112 *PacketType = EthHeader->EType; 00113 break; 00114 00115 default: 00116 TI_DbgPrint(MIN_TRACE, ("Unsupported media.\n")); 00117 00118 /* FIXME: Support other medias */ 00119 return NDIS_STATUS_NOT_ACCEPTED; 00120 } 00121 00122 TI_DbgPrint(DEBUG_DATALINK, ("EtherType (0x%X).\n", *PacketType)); 00123 00124 return NDIS_STATUS_SUCCESS; 00125 } 00126 00127 /* Used by ProtocolReceivePacket for packet type */ 00128 NDIS_STATUS 00129 GetPacketTypeFromNdisPacket(PLAN_ADAPTER Adapter, 00130 PNDIS_PACKET NdisPacket, 00131 PULONG PacketType) 00132 { 00133 PVOID HeaderBuffer; 00134 ULONG BytesCopied; 00135 NDIS_STATUS Status; 00136 00137 HeaderBuffer = ExAllocatePool(NonPagedPool, 00138 Adapter->HeaderSize); 00139 if (!HeaderBuffer) 00140 return NDIS_STATUS_RESOURCES; 00141 00142 /* Copy the media header */ 00143 BytesCopied = CopyPacketToBuffer(HeaderBuffer, 00144 NdisPacket, 00145 0, 00146 Adapter->HeaderSize); 00147 if (BytesCopied != Adapter->HeaderSize) 00148 { 00149 /* Runt frame */ 00150 ExFreePool(HeaderBuffer); 00151 TI_DbgPrint(DEBUG_DATALINK, ("Runt frame (size %d).\n", BytesCopied)); 00152 return NDIS_STATUS_NOT_ACCEPTED; 00153 } 00154 00155 Status = GetPacketTypeFromHeaderBuffer(Adapter, 00156 HeaderBuffer, 00157 BytesCopied, 00158 PacketType); 00159 00160 ExFreePool(HeaderBuffer); 00161 00162 return Status; 00163 } 00164 00165 00166 VOID FreeAdapter( 00167 PLAN_ADAPTER Adapter) 00168 /* 00169 * FUNCTION: Frees memory for a LAN_ADAPTER structure 00170 * ARGUMENTS: 00171 * Adapter = Pointer to LAN_ADAPTER structure to free 00172 */ 00173 { 00174 ExFreePoolWithTag(Adapter, LAN_ADAPTER_TAG); 00175 } 00176 00177 00178 NTSTATUS TcpipLanGetDwordOid 00179 ( PIP_INTERFACE Interface, 00180 NDIS_OID Oid, 00181 PULONG Result ) { 00182 /* Get maximum frame size */ 00183 if( Interface->Context ) { 00184 return NDISCall((PLAN_ADAPTER)Interface->Context, 00185 NdisRequestQueryInformation, 00186 Oid, 00187 Result, 00188 sizeof(ULONG)); 00189 } else switch( Oid ) { /* Loopback Case */ 00190 case OID_GEN_HARDWARE_STATUS: 00191 *Result = NdisHardwareStatusReady; 00192 return STATUS_SUCCESS; 00193 case OID_GEN_MEDIA_CONNECT_STATUS: 00194 *Result = NdisMediaStateConnected; 00195 return STATUS_SUCCESS; 00196 default: 00197 return STATUS_INVALID_PARAMETER; 00198 } 00199 } 00200 00201 00202 VOID NTAPI ProtocolOpenAdapterComplete( 00203 NDIS_HANDLE BindingContext, 00204 NDIS_STATUS Status, 00205 NDIS_STATUS OpenErrorStatus) 00206 /* 00207 * FUNCTION: Called by NDIS to complete opening of an adapter 00208 * ARGUMENTS: 00209 * BindingContext = Pointer to a device context (LAN_ADAPTER) 00210 * Status = Status of the operation 00211 * OpenErrorStatus = Additional status information 00212 */ 00213 { 00214 PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext; 00215 00216 TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); 00217 00218 Adapter->NdisStatus = Status; 00219 00220 KeSetEvent(&Adapter->Event, 0, FALSE); 00221 } 00222 00223 00224 VOID NTAPI ProtocolCloseAdapterComplete( 00225 NDIS_HANDLE BindingContext, 00226 NDIS_STATUS Status) 00227 /* 00228 * FUNCTION: Called by NDIS to complete closing an adapter 00229 * ARGUMENTS: 00230 * BindingContext = Pointer to a device context (LAN_ADAPTER) 00231 * Status = Status of the operation 00232 */ 00233 { 00234 PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext; 00235 00236 TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); 00237 00238 Adapter->NdisStatus = Status; 00239 00240 KeSetEvent(&Adapter->Event, 0, FALSE); 00241 } 00242 00243 00244 VOID NTAPI ProtocolResetComplete( 00245 NDIS_HANDLE BindingContext, 00246 NDIS_STATUS Status) 00247 /* 00248 * FUNCTION: Called by NDIS to complete resetting an adapter 00249 * ARGUMENTS: 00250 * BindingContext = Pointer to a device context (LAN_ADAPTER) 00251 * Status = Status of the operation 00252 */ 00253 { 00254 PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext; 00255 00256 TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); 00257 00258 Adapter->NdisStatus = Status; 00259 00260 KeSetEvent(&Adapter->Event, 0, FALSE); 00261 } 00262 00263 00264 VOID NTAPI ProtocolRequestComplete( 00265 NDIS_HANDLE BindingContext, 00266 PNDIS_REQUEST NdisRequest, 00267 NDIS_STATUS Status) 00268 /* 00269 * FUNCTION: Called by NDIS to complete a request 00270 * ARGUMENTS: 00271 * BindingContext = Pointer to a device context (LAN_ADAPTER) 00272 * NdisRequest = Pointer to an object describing the request 00273 * Status = Status of the operation 00274 */ 00275 { 00276 PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext; 00277 00278 TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); 00279 00280 /* Save status of request and signal an event */ 00281 Adapter->NdisStatus = Status; 00282 00283 KeSetEvent(&Adapter->Event, 0, FALSE); 00284 } 00285 00286 00287 VOID NTAPI ProtocolSendComplete( 00288 NDIS_HANDLE BindingContext, 00289 PNDIS_PACKET Packet, 00290 NDIS_STATUS Status) 00291 /* 00292 * FUNCTION: Called by NDIS to complete sending process 00293 * ARGUMENTS: 00294 * BindingContext = Pointer to a device context (LAN_ADAPTER) 00295 * Packet = Pointer to a packet descriptor 00296 * Status = Status of the operation 00297 */ 00298 { 00299 FreeNdisPacket(Packet); 00300 } 00301 00302 VOID LanReceiveWorker( PVOID Context ) { 00303 ULONG PacketType; 00304 PLAN_WQ_ITEM WorkItem = (PLAN_WQ_ITEM)Context; 00305 PNDIS_PACKET Packet; 00306 PLAN_ADAPTER Adapter; 00307 UINT BytesTransferred; 00308 IP_PACKET IPPacket; 00309 BOOLEAN LegacyReceive; 00310 PIP_INTERFACE Interface; 00311 00312 TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); 00313 00314 Packet = WorkItem->Packet; 00315 Adapter = WorkItem->Adapter; 00316 BytesTransferred = WorkItem->BytesTransferred; 00317 LegacyReceive = WorkItem->LegacyReceive; 00318 00319 ExFreePoolWithTag(WorkItem, WQ_CONTEXT_TAG); 00320 00321 Interface = Adapter->Context; 00322 00323 IPInitializePacket(&IPPacket, 0); 00324 00325 IPPacket.NdisPacket = Packet; 00326 IPPacket.ReturnPacket = !LegacyReceive; 00327 00328 if (LegacyReceive) 00329 { 00330 /* Packet type is precomputed */ 00331 PacketType = PC(IPPacket.NdisPacket)->PacketType; 00332 00333 /* Data is at position 0 */ 00334 IPPacket.Position = 0; 00335 00336 /* Packet size is determined by bytes transferred */ 00337 IPPacket.TotalSize = BytesTransferred; 00338 } 00339 else 00340 { 00341 /* Determine packet type from media header */ 00342 if (GetPacketTypeFromNdisPacket(Adapter, 00343 IPPacket.NdisPacket, 00344 &PacketType) != NDIS_STATUS_SUCCESS) 00345 { 00346 /* Bad packet */ 00347 IPPacket.Free(&IPPacket); 00348 return; 00349 } 00350 00351 /* Data is at the end of the media header */ 00352 IPPacket.Position = Adapter->HeaderSize; 00353 00354 /* Calculate packet size (excluding media header) */ 00355 NdisQueryPacketLength(IPPacket.NdisPacket, &IPPacket.TotalSize); 00356 } 00357 00358 TI_DbgPrint 00359 (DEBUG_DATALINK, 00360 ("Ether Type = %x Total = %d\n", 00361 PacketType, IPPacket.TotalSize)); 00362 00363 /* Update interface stats */ 00364 Interface->Stats.InBytes += IPPacket.TotalSize + Adapter->HeaderSize; 00365 00366 /* NDIS packet is freed in all of these cases */ 00367 switch (PacketType) { 00368 case ETYPE_IPv4: 00369 case ETYPE_IPv6: 00370 TI_DbgPrint(MID_TRACE,("Received IP Packet\n")); 00371 IPReceive(Adapter->Context, &IPPacket); 00372 break; 00373 case ETYPE_ARP: 00374 TI_DbgPrint(MID_TRACE,("Received ARP Packet\n")); 00375 ARPReceive(Adapter->Context, &IPPacket); 00376 break; 00377 default: 00378 IPPacket.Free(&IPPacket); 00379 break; 00380 } 00381 } 00382 00383 VOID LanSubmitReceiveWork( 00384 NDIS_HANDLE BindingContext, 00385 PNDIS_PACKET Packet, 00386 UINT BytesTransferred, 00387 BOOLEAN LegacyReceive) { 00388 PLAN_WQ_ITEM WQItem = ExAllocatePoolWithTag(NonPagedPool, sizeof(LAN_WQ_ITEM), 00389 WQ_CONTEXT_TAG); 00390 PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext; 00391 00392 TI_DbgPrint(DEBUG_DATALINK,("called\n")); 00393 00394 if (!WQItem) return; 00395 00396 WQItem->Packet = Packet; 00397 WQItem->Adapter = Adapter; 00398 WQItem->BytesTransferred = BytesTransferred; 00399 WQItem->LegacyReceive = LegacyReceive; 00400 00401 if (!ChewCreate( LanReceiveWorker, WQItem )) 00402 ExFreePoolWithTag(WQItem, WQ_CONTEXT_TAG); 00403 } 00404 00405 VOID NTAPI ProtocolTransferDataComplete( 00406 NDIS_HANDLE BindingContext, 00407 PNDIS_PACKET Packet, 00408 NDIS_STATUS Status, 00409 UINT BytesTransferred) 00410 /* 00411 * FUNCTION: Called by NDIS to complete reception of data 00412 * ARGUMENTS: 00413 * BindingContext = Pointer to a device context (LAN_ADAPTER) 00414 * Packet = Pointer to a packet descriptor 00415 * Status = Status of the operation 00416 * BytesTransferred = Number of bytes transferred 00417 * NOTES: 00418 * If the packet was successfully received, determine the protocol 00419 * type and pass it to the correct receive handler 00420 */ 00421 { 00422 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); 00423 00424 TI_DbgPrint(DEBUG_DATALINK,("called\n")); 00425 00426 TransferDataCompleteCalled++; 00427 ASSERT(TransferDataCompleteCalled <= TransferDataCalled); 00428 00429 if( Status != NDIS_STATUS_SUCCESS ) return; 00430 00431 LanSubmitReceiveWork(BindingContext, 00432 Packet, 00433 BytesTransferred, 00434 TRUE); 00435 } 00436 00437 INT NTAPI ProtocolReceivePacket( 00438 NDIS_HANDLE BindingContext, 00439 PNDIS_PACKET NdisPacket) 00440 { 00441 PLAN_ADAPTER Adapter = BindingContext; 00442 00443 if (Adapter->State != LAN_STATE_STARTED) { 00444 TI_DbgPrint(DEBUG_DATALINK, ("Adapter is stopped.\n")); 00445 return 0; 00446 } 00447 00448 LanSubmitReceiveWork(BindingContext, 00449 NdisPacket, 00450 0, /* Unused */ 00451 FALSE); 00452 00453 /* Hold 1 reference on this packet */ 00454 return 1; 00455 } 00456 00457 NDIS_STATUS NTAPI ProtocolReceive( 00458 NDIS_HANDLE BindingContext, 00459 NDIS_HANDLE MacReceiveContext, 00460 PVOID HeaderBuffer, 00461 UINT HeaderBufferSize, 00462 PVOID LookaheadBuffer, 00463 UINT LookaheadBufferSize, 00464 UINT PacketSize) 00465 /* 00466 * FUNCTION: Called by NDIS when a packet has been received on the physical link 00467 * ARGUMENTS: 00468 * BindingContext = Pointer to a device context (LAN_ADAPTER) 00469 * MacReceiveContext = Handle used by underlying NIC driver 00470 * HeaderBuffer = Pointer to a buffer containing the packet header 00471 * HeaderBufferSize = Number of bytes in HeaderBuffer 00472 * LookaheadBuffer = Pointer to a buffer containing buffered packet data 00473 * LookaheadBufferSize = Size of LookaheadBuffer. May be less than asked for 00474 * PacketSize = Overall size of the packet (not including header) 00475 * RETURNS: 00476 * Status of operation 00477 */ 00478 { 00479 ULONG PacketType; 00480 UINT BytesTransferred; 00481 PCHAR BufferData; 00482 NDIS_STATUS NdisStatus; 00483 PNDIS_PACKET NdisPacket; 00484 PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext; 00485 00486 TI_DbgPrint(DEBUG_DATALINK, ("Called. (packetsize %d)\n",PacketSize)); 00487 00488 if (Adapter->State != LAN_STATE_STARTED) { 00489 TI_DbgPrint(DEBUG_DATALINK, ("Adapter is stopped.\n")); 00490 return NDIS_STATUS_NOT_ACCEPTED; 00491 } 00492 00493 if (HeaderBufferSize < Adapter->HeaderSize) { 00494 TI_DbgPrint(DEBUG_DATALINK, ("Runt frame received.\n")); 00495 return NDIS_STATUS_NOT_ACCEPTED; 00496 } 00497 00498 NdisStatus = GetPacketTypeFromHeaderBuffer(Adapter, 00499 HeaderBuffer, 00500 HeaderBufferSize, 00501 &PacketType); 00502 if (NdisStatus != NDIS_STATUS_SUCCESS) 00503 return NDIS_STATUS_NOT_ACCEPTED; 00504 00505 TI_DbgPrint(DEBUG_DATALINK, ("Adapter: %x (MTU %d)\n", 00506 Adapter, Adapter->MTU)); 00507 00508 /* Get a transfer data packet */ 00509 NdisStatus = AllocatePacketWithBuffer( &NdisPacket, NULL, 00510 PacketSize ); 00511 if( NdisStatus != NDIS_STATUS_SUCCESS ) { 00512 return NDIS_STATUS_NOT_ACCEPTED; 00513 } 00514 00515 PC(NdisPacket)->PacketType = PacketType; 00516 00517 TI_DbgPrint(DEBUG_DATALINK, ("pretransfer LookaheadBufferSize %d packsize %d\n",LookaheadBufferSize,PacketSize)); 00518 00519 GetDataPtr( NdisPacket, 0, &BufferData, &PacketSize ); 00520 00521 TransferDataCalled++; 00522 00523 if (LookaheadBufferSize == PacketSize) 00524 { 00525 /* Optimized code path for packets that are fully contained in 00526 * the lookahead buffer. */ 00527 NdisCopyLookaheadData(BufferData, 00528 LookaheadBuffer, 00529 LookaheadBufferSize, 00530 Adapter->MacOptions); 00531 } 00532 else 00533 { 00534 NdisTransferData(&NdisStatus, Adapter->NdisHandle, 00535 MacReceiveContext, 0, PacketSize, 00536 NdisPacket, &BytesTransferred); 00537 } 00538 TI_DbgPrint(DEBUG_DATALINK, ("Calling complete\n")); 00539 00540 if (NdisStatus != NDIS_STATUS_PENDING) 00541 ProtocolTransferDataComplete(BindingContext, 00542 NdisPacket, 00543 NdisStatus, 00544 PacketSize); 00545 00546 TI_DbgPrint(DEBUG_DATALINK, ("leaving\n")); 00547 00548 return NDIS_STATUS_SUCCESS; 00549 } 00550 00551 00552 VOID NTAPI ProtocolReceiveComplete( 00553 NDIS_HANDLE BindingContext) 00554 /* 00555 * FUNCTION: Called by NDIS when we're done receiving data 00556 * ARGUMENTS: 00557 * BindingContext = Pointer to a device context (LAN_ADAPTER) 00558 */ 00559 { 00560 TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); 00561 } 00562 00563 BOOLEAN ReadIpConfiguration(PIP_INTERFACE Interface) 00564 { 00565 OBJECT_ATTRIBUTES ObjectAttributes; 00566 HANDLE ParameterHandle; 00567 PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; 00568 WCHAR Buffer[150]; 00569 UNICODE_STRING IPAddress = RTL_CONSTANT_STRING(L"IPAddress"); 00570 UNICODE_STRING Netmask = RTL_CONSTANT_STRING(L"SubnetMask"); 00571 UNICODE_STRING Gateway = RTL_CONSTANT_STRING(L"DefaultGateway"); 00572 UNICODE_STRING EnableDhcp = RTL_CONSTANT_STRING(L"EnableDHCP"); 00573 UNICODE_STRING Prefix = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\"); 00574 UNICODE_STRING TcpipRegistryPath; 00575 UNICODE_STRING RegistryDataU; 00576 ANSI_STRING RegistryDataA; 00577 ULONG Unused; 00578 NTSTATUS Status; 00579 IP_ADDRESS DefaultMask, Router; 00580 00581 AddrInitIPv4(&DefaultMask, 0); 00582 00583 TcpipRegistryPath.MaximumLength = sizeof(WCHAR) * 150; 00584 TcpipRegistryPath.Length = 0; 00585 TcpipRegistryPath.Buffer = Buffer; 00586 00587 /* Build the registry path */ 00588 RtlAppendUnicodeStringToString(&TcpipRegistryPath, &Prefix); 00589 RtlAppendUnicodeStringToString(&TcpipRegistryPath, &Interface->Name); 00590 00591 InitializeObjectAttributes(&ObjectAttributes, 00592 &TcpipRegistryPath, 00593 OBJ_CASE_INSENSITIVE, 00594 0, 00595 NULL); 00596 00597 /* Open a handle to the adapter parameters */ 00598 Status = ZwOpenKey(&ParameterHandle, KEY_READ, &ObjectAttributes); 00599 00600 if (!NT_SUCCESS(Status)) 00601 { 00602 return FALSE; 00603 } 00604 else 00605 { 00606 KeyValueInfo = ExAllocatePool(PagedPool, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 16 * sizeof(WCHAR)); 00607 if (!KeyValueInfo) 00608 { 00609 ZwClose(ParameterHandle); 00610 return FALSE; 00611 } 00612 00613 /* Read the EnableDHCP entry */ 00614 Status = ZwQueryValueKey(ParameterHandle, 00615 &EnableDhcp, 00616 KeyValuePartialInformation, 00617 KeyValueInfo, 00618 sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG), 00619 &Unused); 00620 if (NT_SUCCESS(Status) && KeyValueInfo->DataLength == sizeof(ULONG) && (*(PULONG)KeyValueInfo->Data) == 0) 00621 { 00622 RegistryDataU.MaximumLength = 16 + sizeof(WCHAR); 00623 RegistryDataU.Buffer = (PWCHAR)KeyValueInfo->Data; 00624 00625 /* Read the IP address */ 00626 Status = ZwQueryValueKey(ParameterHandle, 00627 &IPAddress, 00628 KeyValuePartialInformation, 00629 KeyValueInfo, 00630 sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 16 * sizeof(WCHAR), 00631 &Unused); 00632 if (NT_SUCCESS(Status)) 00633 { 00634 RegistryDataU.Length = KeyValueInfo->DataLength; 00635 00636 RtlUnicodeStringToAnsiString(&RegistryDataA, 00637 &RegistryDataU, 00638 TRUE); 00639 00640 AddrInitIPv4(&Interface->Unicast, inet_addr(RegistryDataA.Buffer)); 00641 00642 RtlFreeAnsiString(&RegistryDataA); 00643 } 00644 00645 Status = ZwQueryValueKey(ParameterHandle, 00646 &Netmask, 00647 KeyValuePartialInformation, 00648 KeyValueInfo, 00649 sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 16 * sizeof(WCHAR), 00650 &Unused); 00651 if (NT_SUCCESS(Status)) 00652 { 00653 RegistryDataU.Length = KeyValueInfo->DataLength; 00654 00655 RtlUnicodeStringToAnsiString(&RegistryDataA, 00656 &RegistryDataU, 00657 TRUE); 00658 00659 AddrInitIPv4(&Interface->Netmask, inet_addr(RegistryDataA.Buffer)); 00660 00661 RtlFreeAnsiString(&RegistryDataA); 00662 } 00663 00664 /* We have to wait until both IP address and subnet mask 00665 * are read to add the interface route, but we must do it 00666 * before we add the default gateway */ 00667 if (!AddrIsUnspecified(&Interface->Unicast) && 00668 !AddrIsUnspecified(&Interface->Netmask)) 00669 IPAddInterfaceRoute(Interface); 00670 00671 /* Read default gateway info */ 00672 Status = ZwQueryValueKey(ParameterHandle, 00673 &Gateway, 00674 KeyValuePartialInformation, 00675 KeyValueInfo, 00676 sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 16 * sizeof(WCHAR), 00677 &Unused); 00678 if (NT_SUCCESS(Status)) 00679 { 00680 RegistryDataU.Length = KeyValueInfo->DataLength; 00681 00682 RtlUnicodeStringToAnsiString(&RegistryDataA, 00683 &RegistryDataU, 00684 TRUE); 00685 00686 AddrInitIPv4(&Router, inet_addr(RegistryDataA.Buffer)); 00687 00688 if (!AddrIsUnspecified(&Router)) 00689 RouterCreateRoute(&DefaultMask, &DefaultMask, &Router, Interface, 1); 00690 00691 RtlFreeAnsiString(&RegistryDataA); 00692 } 00693 } 00694 00695 ZwClose(ParameterHandle); 00696 } 00697 00698 return TRUE; 00699 } 00700 00701 BOOLEAN ReconfigureAdapter(PRECONFIGURE_CONTEXT Context) 00702 { 00703 PLAN_ADAPTER Adapter = Context->Adapter; 00704 PIP_INTERFACE Interface = Adapter->Context; 00705 //NDIS_STATUS NdisStatus; 00706 IP_ADDRESS DefaultMask; 00707 00708 /* Initalize the default unspecified address (0.0.0.0) */ 00709 AddrInitIPv4(&DefaultMask, 0); 00710 if (Context->State == LAN_STATE_STARTED && 00711 !Context->Adapter->CompletingReset) 00712 { 00713 /* Read the IP configuration */ 00714 ReadIpConfiguration(Interface); 00715 00716 /* Compute the broadcast address */ 00717 Interface->Broadcast.Type = IP_ADDRESS_V4; 00718 Interface->Broadcast.Address.IPv4Address = Interface->Unicast.Address.IPv4Address | 00719 ~Interface->Netmask.Address.IPv4Address; 00720 } 00721 else if (!Context->Adapter->CompletingReset) 00722 { 00723 /* Clear IP configuration */ 00724 Interface->Unicast = DefaultMask; 00725 Interface->Netmask = DefaultMask; 00726 Interface->Broadcast = DefaultMask; 00727 00728 /* Remove all interface routes */ 00729 RouterRemoveRoutesForInterface(Interface); 00730 00731 /* Destroy all cached neighbors */ 00732 NBDestroyNeighborsForInterface(Interface); 00733 } 00734 00735 Context->Adapter->CompletingReset = FALSE; 00736 00737 /* Update the IP and link status information cached in TCP */ 00738 TCPUpdateInterfaceIPInformation(Interface); 00739 TCPUpdateInterfaceLinkStatus(Interface); 00740 00741 /* We're done here if the adapter isn't connected */ 00742 if (Context->State != LAN_STATE_STARTED) 00743 { 00744 Adapter->State = Context->State; 00745 return TRUE; 00746 } 00747 00748 /* NDIS Bug! */ 00749 #if 0 00750 /* Get maximum link speed */ 00751 NdisStatus = NDISCall(Adapter, 00752 NdisRequestQueryInformation, 00753 OID_GEN_LINK_SPEED, 00754 &Interface->Speed, 00755 sizeof(UINT)); 00756 00757 if (!NT_SUCCESS(NdisStatus)) 00758 Interface->Speed = IP_DEFAULT_LINK_SPEED; 00759 00760 Adapter->Speed = Interface->Speed * 100L; 00761 00762 /* Get maximum frame size */ 00763 NdisStatus = NDISCall(Adapter, 00764 NdisRequestQueryInformation, 00765 OID_GEN_MAXIMUM_FRAME_SIZE, 00766 &Adapter->MTU, 00767 sizeof(UINT)); 00768 if (NdisStatus != NDIS_STATUS_SUCCESS) 00769 return FALSE; 00770 00771 Interface->MTU = Adapter->MTU; 00772 00773 /* Get maximum packet size */ 00774 NdisStatus = NDISCall(Adapter, 00775 NdisRequestQueryInformation, 00776 OID_GEN_MAXIMUM_TOTAL_SIZE, 00777 &Adapter->MaxPacketSize, 00778 sizeof(UINT)); 00779 if (NdisStatus != NDIS_STATUS_SUCCESS) 00780 return FALSE; 00781 #endif 00782 00783 Adapter->State = Context->State; 00784 00785 return TRUE; 00786 } 00787 00788 VOID ReconfigureAdapterWorker(PVOID Context) 00789 { 00790 PRECONFIGURE_CONTEXT ReconfigureContext = Context; 00791 00792 /* Complete the reconfiguration asynchronously */ 00793 ReconfigureAdapter(ReconfigureContext); 00794 00795 /* Free the context */ 00796 ExFreePool(ReconfigureContext); 00797 } 00798 00799 VOID NTAPI ProtocolStatus( 00800 NDIS_HANDLE BindingContext, 00801 NDIS_STATUS GeneralStatus, 00802 PVOID StatusBuffer, 00803 UINT StatusBufferSize) 00804 /* 00805 * FUNCTION: Called by NDIS when the underlying driver has changed state 00806 * ARGUMENTS: 00807 * BindingContext = Pointer to a device context (LAN_ADAPTER) 00808 * GeneralStatus = A general status code 00809 * StatusBuffer = Pointer to a buffer with medium-specific data 00810 * StatusBufferSize = Number of bytes in StatusBuffer 00811 */ 00812 { 00813 PLAN_ADAPTER Adapter = BindingContext; 00814 PRECONFIGURE_CONTEXT Context; 00815 00816 TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); 00817 00818 /* Ignore the status indication if we have no context yet. We'll get another later */ 00819 if (!Adapter->Context) 00820 return; 00821 00822 Context = ExAllocatePool(NonPagedPool, sizeof(RECONFIGURE_CONTEXT)); 00823 if (!Context) 00824 return; 00825 00826 Context->Adapter = Adapter; 00827 00828 switch(GeneralStatus) 00829 { 00830 case NDIS_STATUS_MEDIA_CONNECT: 00831 DbgPrint("NDIS_STATUS_MEDIA_CONNECT\n"); 00832 00833 if (Adapter->State == LAN_STATE_STARTED) 00834 { 00835 ExFreePool(Context); 00836 return; 00837 } 00838 00839 Context->State = LAN_STATE_STARTED; 00840 break; 00841 00842 case NDIS_STATUS_MEDIA_DISCONNECT: 00843 DbgPrint("NDIS_STATUS_MEDIA_DISCONNECT\n"); 00844 00845 if (Adapter->State == LAN_STATE_STOPPED) 00846 { 00847 ExFreePool(Context); 00848 return; 00849 } 00850 00851 Context->State = LAN_STATE_STOPPED; 00852 break; 00853 00854 case NDIS_STATUS_RESET_START: 00855 Adapter->OldState = Adapter->State; 00856 Adapter->State = LAN_STATE_RESETTING; 00857 /* Nothing else to do here */ 00858 ExFreePool(Context); 00859 return; 00860 00861 case NDIS_STATUS_RESET_END: 00862 Adapter->CompletingReset = TRUE; 00863 Context->State = Adapter->OldState; 00864 break; 00865 00866 default: 00867 DbgPrint("Unhandled status: %x", GeneralStatus); 00868 ExFreePool(Context); 00869 return; 00870 } 00871 00872 /* Queue the work item */ 00873 if (!ChewCreate(ReconfigureAdapterWorker, Context)) 00874 ExFreePool(Context); 00875 } 00876 00877 VOID NTAPI ProtocolStatusComplete(NDIS_HANDLE NdisBindingContext) 00878 /* 00879 * FUNCTION: Called by NDIS when a status-change has occurred 00880 * ARGUMENTS: 00881 * BindingContext = Pointer to a device context (LAN_ADAPTER) 00882 */ 00883 { 00884 TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); 00885 } 00886 00887 NDIS_STATUS NTAPI 00888 ProtocolPnPEvent( 00889 NDIS_HANDLE NdisBindingContext, 00890 PNET_PNP_EVENT PnPEvent) 00891 { 00892 switch(PnPEvent->NetEvent) 00893 { 00894 case NetEventSetPower: 00895 DbgPrint("Device transitioned to power state %ld\n", PnPEvent->Buffer); 00896 return NDIS_STATUS_SUCCESS; 00897 00898 case NetEventQueryPower: 00899 DbgPrint("Device wants to go into power state %ld\n", PnPEvent->Buffer); 00900 return NDIS_STATUS_SUCCESS; 00901 00902 case NetEventQueryRemoveDevice: 00903 DbgPrint("Device is about to be removed\n"); 00904 return NDIS_STATUS_SUCCESS; 00905 00906 case NetEventCancelRemoveDevice: 00907 DbgPrint("Device removal cancelled\n"); 00908 return NDIS_STATUS_SUCCESS; 00909 00910 default: 00911 DbgPrint("Unhandled event type: %ld\n", PnPEvent->NetEvent); 00912 return NDIS_STATUS_SUCCESS; 00913 } 00914 } 00915 00916 VOID NTAPI ProtocolBindAdapter( 00917 OUT PNDIS_STATUS Status, 00918 IN NDIS_HANDLE BindContext, 00919 IN PNDIS_STRING DeviceName, 00920 IN PVOID SystemSpecific1, 00921 IN PVOID SystemSpecific2) 00922 /* 00923 * FUNCTION: Called by NDIS during NdisRegisterProtocol to set up initial 00924 * bindings, and periodically thereafer as new adapters come online 00925 * ARGUMENTS: 00926 * Status: Return value to NDIS 00927 * BindContext: Handle provided by NDIS to track pending binding operations 00928 * DeviceName: Name of the miniport device to bind to 00929 * SystemSpecific1: Pointer to a registry path with protocol-specific configuration information 00930 * SystemSpecific2: Unused & must not be touched 00931 */ 00932 { 00933 TI_DbgPrint(DEBUG_DATALINK, ("Called with registry path %wZ for %wZ\n", SystemSpecific1, DeviceName)); 00934 *Status = LANRegisterAdapter(DeviceName, SystemSpecific1); 00935 } 00936 00937 00938 VOID LANTransmit( 00939 PVOID Context, 00940 PNDIS_PACKET NdisPacket, 00941 UINT Offset, 00942 PVOID LinkAddress, 00943 USHORT Type) 00944 /* 00945 * FUNCTION: Transmits a packet 00946 * ARGUMENTS: 00947 * Context = Pointer to context information (LAN_ADAPTER) 00948 * NdisPacket = Pointer to NDIS packet to send 00949 * Offset = Offset in packet where data starts 00950 * LinkAddress = Pointer to link address of destination (NULL = broadcast) 00951 * Type = LAN protocol type (LAN_PROTO_*) 00952 */ 00953 { 00954 NDIS_STATUS NdisStatus; 00955 PETH_HEADER EHeader; 00956 PCHAR Data, OldData; 00957 UINT Size, OldSize; 00958 PLAN_ADAPTER Adapter = (PLAN_ADAPTER)Context; 00959 KIRQL OldIrql; 00960 PNDIS_PACKET XmitPacket; 00961 PIP_INTERFACE Interface = Adapter->Context; 00962 00963 TI_DbgPrint(DEBUG_DATALINK, 00964 ("Called( NdisPacket %x, Offset %d, Adapter %x )\n", 00965 NdisPacket, Offset, Adapter)); 00966 00967 if (Adapter->State != LAN_STATE_STARTED) { 00968 (*PC(NdisPacket)->DLComplete)(PC(NdisPacket)->Context, NdisPacket, NDIS_STATUS_NOT_ACCEPTED); 00969 return; 00970 } 00971 00972 TI_DbgPrint(DEBUG_DATALINK, 00973 ("Adapter Address [%02x %02x %02x %02x %02x %02x]\n", 00974 Adapter->HWAddress[0] & 0xff, 00975 Adapter->HWAddress[1] & 0xff, 00976 Adapter->HWAddress[2] & 0xff, 00977 Adapter->HWAddress[3] & 0xff, 00978 Adapter->HWAddress[4] & 0xff, 00979 Adapter->HWAddress[5] & 0xff)); 00980 00981 GetDataPtr( NdisPacket, 0, &OldData, &OldSize ); 00982 00983 NdisStatus = AllocatePacketWithBuffer(&XmitPacket, NULL, OldSize + Adapter->HeaderSize); 00984 if (NdisStatus != NDIS_STATUS_SUCCESS) { 00985 (*PC(NdisPacket)->DLComplete)(PC(NdisPacket)->Context, NdisPacket, NDIS_STATUS_RESOURCES); 00986 return; 00987 } 00988 00989 GetDataPtr(XmitPacket, 0, &Data, &Size); 00990 00991 RtlCopyMemory(Data + Adapter->HeaderSize, OldData, OldSize); 00992 00993 (*PC(NdisPacket)->DLComplete)(PC(NdisPacket)->Context, NdisPacket, NDIS_STATUS_SUCCESS); 00994 00995 switch (Adapter->Media) { 00996 case NdisMedium802_3: 00997 EHeader = (PETH_HEADER)Data; 00998 00999 if (LinkAddress) { 01000 /* Unicast address */ 01001 RtlCopyMemory(EHeader->DstAddr, LinkAddress, IEEE_802_ADDR_LENGTH); 01002 } else { 01003 /* Broadcast address */ 01004 RtlFillMemory(EHeader->DstAddr, IEEE_802_ADDR_LENGTH, 0xFF); 01005 } 01006 01007 RtlCopyMemory(EHeader->SrcAddr, Adapter->HWAddress, IEEE_802_ADDR_LENGTH); 01008 01009 switch (Type) { 01010 case LAN_PROTO_IPv4: 01011 EHeader->EType = ETYPE_IPv4; 01012 break; 01013 case LAN_PROTO_ARP: 01014 EHeader->EType = ETYPE_ARP; 01015 break; 01016 case LAN_PROTO_IPv6: 01017 EHeader->EType = ETYPE_IPv6; 01018 break; 01019 default: 01020 ASSERT(FALSE); 01021 return; 01022 } 01023 break; 01024 01025 default: 01026 /* FIXME: Support other medias */ 01027 break; 01028 } 01029 01030 TI_DbgPrint( MID_TRACE, ("LinkAddress: %x\n", LinkAddress)); 01031 if( LinkAddress ) { 01032 TI_DbgPrint 01033 ( MID_TRACE, 01034 ("Link Address [%02x %02x %02x %02x %02x %02x]\n", 01035 ((PCHAR)LinkAddress)[0] & 0xff, 01036 ((PCHAR)LinkAddress)[1] & 0xff, 01037 ((PCHAR)LinkAddress)[2] & 0xff, 01038 ((PCHAR)LinkAddress)[3] & 0xff, 01039 ((PCHAR)LinkAddress)[4] & 0xff, 01040 ((PCHAR)LinkAddress)[5] & 0xff)); 01041 } 01042 01043 if (Adapter->MTU < Size) { 01044 /* This is NOT a pointer. MSDN explicitly says so. */ 01045 NDIS_PER_PACKET_INFO_FROM_PACKET(NdisPacket, 01046 TcpLargeSendPacketInfo) = (PVOID)((ULONG_PTR)Adapter->MTU); 01047 } 01048 01049 /* Update interface stats */ 01050 Interface->Stats.OutBytes += Size; 01051 01052 TcpipAcquireSpinLock( &Adapter->Lock, &OldIrql ); 01053 TI_DbgPrint(MID_TRACE, ("NdisSend\n")); 01054 NdisSend(&NdisStatus, Adapter->NdisHandle, XmitPacket); 01055 TI_DbgPrint(MID_TRACE, ("NdisSend %s\n", 01056 NdisStatus == NDIS_STATUS_PENDING ? 01057 "Pending" : "Complete")); 01058 TcpipReleaseSpinLock( &Adapter->Lock, OldIrql ); 01059 01060 /* I had a talk with vizzini: these really ought to be here. 01061 * we're supposed to see these completed by ndis *only* when 01062 * status_pending is returned. Note that this is different from 01063 * the situation with IRPs. */ 01064 if (NdisStatus != NDIS_STATUS_PENDING) 01065 ProtocolSendComplete((NDIS_HANDLE)Context, XmitPacket, NdisStatus); 01066 } 01067 01068 static NTSTATUS 01069 OpenRegistryKey( PNDIS_STRING RegistryPath, PHANDLE RegHandle ) { 01070 OBJECT_ATTRIBUTES Attributes; 01071 NTSTATUS Status; 01072 01073 InitializeObjectAttributes(&Attributes, RegistryPath, OBJ_CASE_INSENSITIVE, 0, 0); 01074 Status = ZwOpenKey(RegHandle, KEY_ALL_ACCESS, &Attributes); 01075 return Status; 01076 } 01077 01078 static NTSTATUS ReadStringFromRegistry( HANDLE RegHandle, 01079 PWCHAR RegistryValue, 01080 PUNICODE_STRING String ) { 01081 UNICODE_STRING ValueName; 01082 UNICODE_STRING UnicodeString; 01083 NTSTATUS Status; 01084 ULONG ResultLength; 01085 UCHAR buf[1024]; 01086 PKEY_VALUE_PARTIAL_INFORMATION Information = (PKEY_VALUE_PARTIAL_INFORMATION)buf; 01087 01088 RtlInitUnicodeString(&ValueName, RegistryValue); 01089 Status = 01090 ZwQueryValueKey(RegHandle, 01091 &ValueName, 01092 KeyValuePartialInformation, 01093 Information, 01094 sizeof(buf), 01095 &ResultLength); 01096 01097 if (!NT_SUCCESS(Status)) 01098 return Status; 01099 /* IP address is stored as a REG_MULTI_SZ - we only pay attention to the first one though */ 01100 TI_DbgPrint(MIN_TRACE, ("Information DataLength: 0x%x\n", Information->DataLength)); 01101 01102 UnicodeString.Buffer = (PWCHAR)&Information->Data; 01103 UnicodeString.Length = Information->DataLength - sizeof(WCHAR); 01104 UnicodeString.MaximumLength = Information->DataLength; 01105 01106 String->Buffer = 01107 (PWCHAR)ExAllocatePool( NonPagedPool, 01108 UnicodeString.MaximumLength + sizeof(WCHAR) ); 01109 01110 if( !String->Buffer ) return STATUS_NO_MEMORY; 01111 01112 String->MaximumLength = UnicodeString.MaximumLength; 01113 RtlCopyUnicodeString( String, &UnicodeString ); 01114 01115 return STATUS_SUCCESS; 01116 } 01117 01118 /* 01119 * Utility to copy and append two unicode strings. 01120 * 01121 * IN OUT PUNICODE_STRING ResultFirst -> First string and result 01122 * IN PUNICODE_STRING Second -> Second string to append 01123 * IN BOOL Deallocate -> TRUE: Deallocate First string before 01124 * overwriting. 01125 * 01126 * Returns NTSTATUS. 01127 */ 01128 01129 NTSTATUS NTAPI AppendUnicodeString(PUNICODE_STRING ResultFirst, 01130 PUNICODE_STRING Second, 01131 BOOLEAN Deallocate) { 01132 NTSTATUS Status; 01133 UNICODE_STRING Ustr = *ResultFirst; 01134 PWSTR new_string = ExAllocatePool 01135 (PagedPool, 01136 (ResultFirst->Length + Second->Length + sizeof(WCHAR))); 01137 if( !new_string ) { 01138 return STATUS_NO_MEMORY; 01139 } 01140 memcpy( new_string, ResultFirst->Buffer, ResultFirst->Length ); 01141 memcpy( new_string + ResultFirst->Length / sizeof(WCHAR), 01142 Second->Buffer, Second->Length ); 01143 if( Deallocate ) RtlFreeUnicodeString(ResultFirst); 01144 ResultFirst->Length = Ustr.Length + Second->Length; 01145 ResultFirst->MaximumLength = ResultFirst->Length; 01146 new_string[ResultFirst->Length / sizeof(WCHAR)] = 0; 01147 Status = RtlCreateUnicodeString(ResultFirst,new_string) ? 01148 STATUS_SUCCESS : STATUS_NO_MEMORY; 01149 ExFreePool(new_string); 01150 return Status; 01151 } 01152 01153 static NTSTATUS CheckForDeviceDesc( PUNICODE_STRING EnumKeyName, 01154 PUNICODE_STRING TargetKeyName, 01155 PUNICODE_STRING Name, 01156 PUNICODE_STRING DeviceDesc ) { 01157 UNICODE_STRING RootDevice = { 0, 0, NULL }, LinkageKeyName = { 0, 0, NULL }; 01158 UNICODE_STRING DescKeyName = { 0, 0, NULL }, Linkage = { 0, 0, NULL }; 01159 UNICODE_STRING BackSlash = { 0, 0, NULL }; 01160 HANDLE DescKey = NULL, LinkageKey = NULL; 01161 NTSTATUS Status; 01162 01163 TI_DbgPrint(DEBUG_DATALINK,("EnumKeyName %wZ\n", EnumKeyName)); 01164 01165 RtlInitUnicodeString(&BackSlash, L"\\"); 01166 RtlInitUnicodeString(&Linkage, L"\\Linkage"); 01167 01168 RtlInitUnicodeString(&DescKeyName, L""); 01169 AppendUnicodeString( &DescKeyName, EnumKeyName, FALSE ); 01170 AppendUnicodeString( &DescKeyName, &BackSlash, TRUE ); 01171 AppendUnicodeString( &DescKeyName, TargetKeyName, TRUE ); 01172 01173 RtlInitUnicodeString(&LinkageKeyName, L""); 01174 AppendUnicodeString( &LinkageKeyName, &DescKeyName, FALSE ); 01175 AppendUnicodeString( &LinkageKeyName, &Linkage, TRUE ); 01176 01177 Status = OpenRegistryKey( &LinkageKeyName, &LinkageKey ); 01178 if( !NT_SUCCESS(Status) ) goto cleanup; 01179 01180 Status = ReadStringFromRegistry( LinkageKey, L"RootDevice", &RootDevice ); 01181 if( !NT_SUCCESS(Status) ) goto cleanup; 01182 01183 if( RtlCompareUnicodeString( &RootDevice, Name, TRUE ) == 0 ) { 01184 Status = OpenRegistryKey( &DescKeyName, &DescKey ); 01185 if( !NT_SUCCESS(Status) ) goto cleanup; 01186 01187 Status = ReadStringFromRegistry( DescKey, L"DriverDesc", DeviceDesc ); 01188 if( !NT_SUCCESS(Status) ) goto cleanup; 01189 01190 TI_DbgPrint(DEBUG_DATALINK,("ADAPTER DESC: %wZ\n", DeviceDesc)); 01191 } else Status = STATUS_UNSUCCESSFUL; 01192 01193 cleanup: 01194 RtlFreeUnicodeString( &RootDevice ); 01195 RtlFreeUnicodeString( &LinkageKeyName ); 01196 RtlFreeUnicodeString( &DescKeyName ); 01197 if( LinkageKey ) NtClose( LinkageKey ); 01198 if( DescKey ) NtClose( DescKey ); 01199 01200 TI_DbgPrint(DEBUG_DATALINK,("Returning %x\n", Status)); 01201 01202 return Status; 01203 } 01204 01205 static NTSTATUS FindDeviceDescForAdapter( PUNICODE_STRING Name, 01206 PUNICODE_STRING DeviceDesc ) { 01207 UNICODE_STRING EnumKeyName, TargetKeyName; 01208 HANDLE EnumKey; 01209 NTSTATUS Status; 01210 ULONG i; 01211 KEY_BASIC_INFORMATION *Kbio = 01212 ExAllocatePool(NonPagedPool, sizeof(KEY_BASIC_INFORMATION)); 01213 ULONG KbioLength = sizeof(KEY_BASIC_INFORMATION), ResultLength; 01214 01215 RtlInitUnicodeString( DeviceDesc, NULL ); 01216 01217 if( !Kbio ) return STATUS_INSUFFICIENT_RESOURCES; 01218 01219 RtlInitUnicodeString 01220 (&EnumKeyName, CCS_ROOT L"\\Control\\Class\\" TCPIP_GUID); 01221 01222 Status = OpenRegistryKey( &EnumKeyName, &EnumKey ); 01223 01224 if( !NT_SUCCESS(Status) ) { 01225 TI_DbgPrint(DEBUG_DATALINK,("Couldn't open Enum key %wZ: %x\n", 01226 &EnumKeyName, Status)); 01227 ExFreePool( Kbio ); 01228 return Status; 01229 } 01230 01231 for( i = 0; NT_SUCCESS(Status); i++ ) { 01232 Status = ZwEnumerateKey( EnumKey, i, KeyBasicInformation, 01233 Kbio, KbioLength, &ResultLength ); 01234 01235 if( Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW ) { 01236 ExFreePool( Kbio ); 01237 KbioLength = ResultLength; 01238 Kbio = ExAllocatePool( NonPagedPool, KbioLength ); 01239 if( !Kbio ) { 01240 TI_DbgPrint(DEBUG_DATALINK,("Failed to allocate memory\n")); 01241 NtClose( EnumKey ); 01242 return STATUS_NO_MEMORY; 01243 } 01244 01245 Status = ZwEnumerateKey( EnumKey, i, KeyBasicInformation, 01246 Kbio, KbioLength, &ResultLength ); 01247 01248 if( !NT_SUCCESS(Status) ) { 01249 TI_DbgPrint(DEBUG_DATALINK,("Couldn't enum key child %d\n", i)); 01250 NtClose( EnumKey ); 01251 ExFreePool( Kbio ); 01252 return Status; 01253 } 01254 } 01255 01256 if( NT_SUCCESS(Status) ) { 01257 TargetKeyName.Length = TargetKeyName.MaximumLength = 01258 Kbio->NameLength; 01259 TargetKeyName.Buffer = Kbio->Name; 01260 01261 Status = CheckForDeviceDesc 01262 ( &EnumKeyName, &TargetKeyName, Name, DeviceDesc ); 01263 if( NT_SUCCESS(Status) ) { 01264 NtClose( EnumKey ); 01265 ExFreePool( Kbio ); 01266 return Status; 01267 } else Status = STATUS_SUCCESS; 01268 } 01269 } 01270 01271 NtClose( EnumKey ); 01272 ExFreePool( Kbio ); 01273 return STATUS_UNSUCCESSFUL; 01274 } 01275 01276 VOID GetName( PUNICODE_STRING RegistryKey, 01277 PUNICODE_STRING OutName ) { 01278 PWCHAR Ptr; 01279 UNICODE_STRING PartialRegistryKey; 01280 01281 PartialRegistryKey.Buffer = 01282 RegistryKey->Buffer + wcslen(CCS_ROOT L"\\Services\\"); 01283 Ptr = PartialRegistryKey.Buffer; 01284 01285 while( *Ptr != L'\\' && 01286 ((PCHAR)Ptr) < ((PCHAR)RegistryKey->Buffer) + RegistryKey->Length ) 01287 Ptr++; 01288 01289 PartialRegistryKey.Length = PartialRegistryKey.MaximumLength = 01290 (Ptr - PartialRegistryKey.Buffer) * sizeof(WCHAR); 01291 01292 RtlInitUnicodeString( OutName, L"" ); 01293 AppendUnicodeString( OutName, &PartialRegistryKey, FALSE ); 01294 } 01295 01296 BOOLEAN BindAdapter( 01297 PLAN_ADAPTER Adapter, 01298 PNDIS_STRING RegistryPath) 01299 /* 01300 * FUNCTION: Binds a LAN adapter to IP layer 01301 * ARGUMENTS: 01302 * Adapter = Pointer to LAN_ADAPTER structure 01303 * NOTES: 01304 * We set the lookahead buffer size, set the packet filter and 01305 * bind the adapter to IP layer 01306 */ 01307 { 01308 PIP_INTERFACE IF; 01309 NDIS_STATUS NdisStatus; 01310 LLIP_BIND_INFO BindInfo; 01311 ULONG Lookahead = LOOKAHEAD_SIZE; 01312 NTSTATUS Status; 01313 NDIS_MEDIA_STATE MediaState; 01314 01315 TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); 01316 01317 Adapter->State = LAN_STATE_OPENING; 01318 01319 NdisStatus = NDISCall(Adapter, 01320 NdisRequestSetInformation, 01321 OID_GEN_CURRENT_LOOKAHEAD, 01322 &Lookahead, 01323 sizeof(ULONG)); 01324 if (NdisStatus != NDIS_STATUS_SUCCESS) { 01325 TI_DbgPrint(DEBUG_DATALINK, ("Could not set lookahead buffer size (0x%X).\n", NdisStatus)); 01326 return FALSE; 01327 } 01328 01329 /* Bind the adapter to IP layer */ 01330 BindInfo.Context = Adapter; 01331 BindInfo.HeaderSize = Adapter->HeaderSize; 01332 BindInfo.MinFrameSize = Adapter->MinFrameSize; 01333 BindInfo.Address = (PUCHAR)&Adapter->HWAddress; 01334 BindInfo.AddressLength = Adapter->HWAddressLength; 01335 BindInfo.Transmit = LANTransmit; 01336 01337 IF = IPCreateInterface(&BindInfo); 01338 01339 if (!IF) { 01340 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); 01341 return FALSE; 01342 } 01343 01344 /* 01345 * Query per-adapter configuration from the registry 01346 * In case anyone is curious: there *is* an Ndis configuration api 01347 * for this sort of thing, but it doesn't really support things like 01348 * REG_MULTI_SZ very well, and there is a note in the DDK that says that 01349 * protocol drivers developed for win2k and above just use the native 01350 * services (ZwOpenKey, etc). 01351 */ 01352 01353 GetName( RegistryPath, &IF->Name ); 01354 01355 Status = FindDeviceDescForAdapter( &IF->Name, &IF->Description ); 01356 if (!NT_SUCCESS(Status)) { 01357 TI_DbgPrint(MIN_TRACE, ("Failed to get device description.\n")); 01358 IPDestroyInterface(IF); 01359 return FALSE; 01360 } 01361 01362 TI_DbgPrint(DEBUG_DATALINK,("Adapter Description: %wZ\n", 01363 &IF->Description)); 01364 01365 /* Get maximum link speed */ 01366 NdisStatus = NDISCall(Adapter, 01367 NdisRequestQueryInformation, 01368 OID_GEN_LINK_SPEED, 01369 &IF->Speed, 01370 sizeof(UINT)); 01371 01372 if (!NT_SUCCESS(NdisStatus)) 01373 IF->Speed = IP_DEFAULT_LINK_SPEED; 01374 01375 Adapter->Speed = IF->Speed * 100L; 01376 01377 /* Get maximum frame size */ 01378 NdisStatus = NDISCall(Adapter, 01379 NdisRequestQueryInformation, 01380 OID_GEN_MAXIMUM_FRAME_SIZE, 01381 &Adapter->MTU, 01382 sizeof(UINT)); 01383 if (NdisStatus != NDIS_STATUS_SUCCESS) 01384 return FALSE; 01385 01386 IF->MTU = Adapter->MTU; 01387 01388 /* Get maximum packet size */ 01389 NdisStatus = NDISCall(Adapter, 01390 NdisRequestQueryInformation, 01391 OID_GEN_MAXIMUM_TOTAL_SIZE, 01392 &Adapter->MaxPacketSize, 01393 sizeof(UINT)); 01394 if (NdisStatus != NDIS_STATUS_SUCCESS) 01395 return FALSE; 01396 01397 /* Register interface with IP layer */ 01398 IPRegisterInterface(IF); 01399 01400 /* Store adapter context */ 01401 Adapter->Context = IF; 01402 01403 /* Get the media state */ 01404 NdisStatus = NDISCall(Adapter, 01405 NdisRequestQueryInformation, 01406 OID_GEN_MEDIA_CONNECT_STATUS, 01407 &MediaState, 01408 sizeof(MediaState)); 01409 if (NdisStatus != NDIS_STATUS_SUCCESS) { 01410 TI_DbgPrint(DEBUG_DATALINK, ("Could not query media status (0x%X).\n", NdisStatus)); 01411 IPUnregisterInterface(IF); 01412 IPDestroyInterface(IF); 01413 return FALSE; 01414 } 01415 01416 /* Indicate the current media state */ 01417 ProtocolStatus(Adapter, 01418 (MediaState == NdisMediaStateConnected) ? NDIS_STATUS_MEDIA_CONNECT : NDIS_STATUS_MEDIA_DISCONNECT, 01419 NULL, 0); 01420 01421 /* Set packet filter so we can send and receive packets */ 01422 NdisStatus = NDISCall(Adapter, 01423 NdisRequestSetInformation, 01424 OID_GEN_CURRENT_PACKET_FILTER, 01425 &Adapter->PacketFilter, 01426 sizeof(UINT)); 01427 01428 if (NdisStatus != NDIS_STATUS_SUCCESS) { 01429 TI_DbgPrint(DEBUG_DATALINK, ("Could not set packet filter (0x%X).\n", NdisStatus)); 01430 IPUnregisterInterface(IF); 01431 IPDestroyInterface(IF); 01432 return FALSE; 01433 } 01434 01435 return TRUE; 01436 } 01437 01438 01439 VOID UnbindAdapter( 01440 PLAN_ADAPTER Adapter) 01441 /* 01442 * FUNCTION: Unbinds a LAN adapter from IP layer 01443 * ARGUMENTS: 01444 * Adapter = Pointer to LAN_ADAPTER structure 01445 */ 01446 { 01447 TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); 01448 01449 if (Adapter->State == LAN_STATE_STARTED) { 01450 PIP_INTERFACE IF = Adapter->Context; 01451 01452 IPUnregisterInterface(IF); 01453 01454 IPDestroyInterface(IF); 01455 } 01456 } 01457 01458 01459 NDIS_STATUS LANRegisterAdapter( 01460 PNDIS_STRING AdapterName, 01461 PNDIS_STRING RegistryPath) 01462 /* 01463 * FUNCTION: Registers protocol with an NDIS adapter 01464 * ARGUMENTS: 01465 * AdapterName = Pointer to string with name of adapter to register 01466 * Adapter = Address of pointer to a LAN_ADAPTER structure 01467 * RETURNS: 01468 * Status of operation 01469 */ 01470 { 01471 PLAN_ADAPTER IF; 01472 NDIS_STATUS NdisStatus; 01473 NDIS_STATUS OpenStatus; 01474 UINT MediaIndex; 01475 NDIS_MEDIUM MediaArray[MAX_MEDIA]; 01476 UINT AddressOID; 01477 01478 TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); 01479 01480 IF = ExAllocatePoolWithTag(NonPagedPool, sizeof(LAN_ADAPTER), LAN_ADAPTER_TAG); 01481 if (!IF) { 01482 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); 01483 return NDIS_STATUS_RESOURCES; 01484 } 01485 01486 RtlZeroMemory(IF, sizeof(LAN_ADAPTER)); 01487 01488 /* Put adapter in stopped state */ 01489 IF->State = LAN_STATE_STOPPED; 01490 01491 /* Initialize protecting spin lock */ 01492 KeInitializeSpinLock(&IF->Lock); 01493 01494 KeInitializeEvent(&IF->Event, SynchronizationEvent, FALSE); 01495 01496 /* Initialize array with media IDs we support */ 01497 MediaArray[MEDIA_ETH] = NdisMedium802_3; 01498 01499 TI_DbgPrint(DEBUG_DATALINK,("opening adapter %wZ\n", AdapterName)); 01500 /* Open the adapter. */ 01501 NdisOpenAdapter(&NdisStatus, 01502 &OpenStatus, 01503 &IF->NdisHandle, 01504 &MediaIndex, 01505 MediaArray, 01506 MAX_MEDIA, 01507 NdisProtocolHandle, 01508 IF, 01509 AdapterName, 01510 0, 01511 NULL); 01512 01513 /* Wait until the adapter is opened */ 01514 if (NdisStatus == NDIS_STATUS_PENDING) 01515 KeWaitForSingleObject(&IF->Event, UserRequest, KernelMode, FALSE, NULL); 01516 else if (NdisStatus != NDIS_STATUS_SUCCESS) { 01517 TI_DbgPrint(DEBUG_DATALINK,("denying adapter %wZ\n", AdapterName)); 01518 ExFreePoolWithTag(IF, LAN_ADAPTER_TAG); 01519 return NdisStatus; 01520 } 01521 01522 IF->Media = MediaArray[MediaIndex]; 01523 01524 /* Fill LAN_ADAPTER structure with some adapter specific information */ 01525 switch (IF->Media) { 01526 case NdisMedium802_3: 01527 IF->HWAddressLength = IEEE_802_ADDR_LENGTH; 01528 IF->BCastMask = BCAST_ETH_MASK; 01529 IF->BCastCheck = BCAST_ETH_CHECK; 01530 IF->BCastOffset = BCAST_ETH_OFFSET; 01531 IF->HeaderSize = sizeof(ETH_HEADER); 01532 IF->MinFrameSize = 60; 01533 AddressOID = OID_802_3_CURRENT_ADDRESS; 01534 IF->PacketFilter = 01535 NDIS_PACKET_TYPE_BROADCAST | 01536 NDIS_PACKET_TYPE_DIRECTED | 01537 NDIS_PACKET_TYPE_MULTICAST; 01538 break; 01539 01540 default: 01541 /* Unsupported media */ 01542 TI_DbgPrint(MIN_TRACE, ("Unsupported media.\n")); 01543 ExFreePoolWithTag(IF, LAN_ADAPTER_TAG); 01544 return NDIS_STATUS_NOT_SUPPORTED; 01545 } 01546 01547 /* Get maximum number of packets we can pass to NdisSend(Packets) at one time */ 01548 NdisStatus = NDISCall(IF, 01549 NdisRequestQueryInformation, 01550 OID_GEN_MAXIMUM_SEND_PACKETS, 01551 &IF->MaxSendPackets, 01552 sizeof(UINT)); 01553 if (NdisStatus != NDIS_STATUS_SUCCESS) 01554 /* Legacy NIC drivers may not support this query, if it fails we 01555 assume it can send at least one packet per call to NdisSend(Packets) */ 01556 IF->MaxSendPackets = 1; 01557 01558 /* Get current hardware address */ 01559 NdisStatus = NDISCall(IF, 01560 NdisRequestQueryInformation, 01561 AddressOID, 01562 &IF->HWAddress, 01563 IF->HWAddressLength); 01564 if (NdisStatus != NDIS_STATUS_SUCCESS) { 01565 TI_DbgPrint(MIN_TRACE, ("Query for current hardware address failed.\n")); 01566 ExFreePoolWithTag(IF, LAN_ADAPTER_TAG); 01567 return NdisStatus; 01568 } 01569 01570 /* Bind adapter to IP layer */ 01571 if( !BindAdapter(IF, RegistryPath) ) { 01572 TI_DbgPrint(DEBUG_DATALINK,("denying adapter %wZ (BindAdapter)\n", AdapterName)); 01573 ExFreePoolWithTag(IF, LAN_ADAPTER_TAG); 01574 return NDIS_STATUS_NOT_ACCEPTED; 01575 } 01576 01577 /* Add adapter to the adapter list */ 01578 ExInterlockedInsertTailList(&AdapterListHead, 01579 &IF->ListEntry, 01580 &AdapterListLock); 01581 01582 TI_DbgPrint(DEBUG_DATALINK, ("Leaving.\n")); 01583 01584 return NDIS_STATUS_SUCCESS; 01585 } 01586 01587 01588 NDIS_STATUS LANUnregisterAdapter( 01589 PLAN_ADAPTER Adapter) 01590 /* 01591 * FUNCTION: Unregisters protocol with NDIS adapter 01592 * ARGUMENTS: 01593 * Adapter = Pointer to a LAN_ADAPTER structure 01594 * RETURNS: 01595 * Status of operation 01596 */ 01597 { 01598 KIRQL OldIrql; 01599 NDIS_HANDLE NdisHandle; 01600 NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS; 01601 01602 TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); 01603 01604 /* Unlink the adapter from the list */ 01605 RemoveEntryList(&Adapter->ListEntry); 01606 01607 /* Unbind adapter from IP layer */ 01608 UnbindAdapter(Adapter); 01609 01610 TcpipAcquireSpinLock(&Adapter->Lock, &OldIrql); 01611 NdisHandle = Adapter->NdisHandle; 01612 if (NdisHandle) { 01613 Adapter->NdisHandle = NULL; 01614 TcpipReleaseSpinLock(&Adapter->Lock, OldIrql); 01615 01616 NdisCloseAdapter(&NdisStatus, NdisHandle); 01617 if (NdisStatus == NDIS_STATUS_PENDING) { 01618 TcpipWaitForSingleObject(&Adapter->Event, 01619 UserRequest, 01620 KernelMode, 01621 FALSE, 01622 NULL); 01623 NdisStatus = Adapter->NdisStatus; 01624 } 01625 } else 01626 TcpipReleaseSpinLock(&Adapter->Lock, OldIrql); 01627 01628 FreeAdapter(Adapter); 01629 01630 return NdisStatus; 01631 } 01632 01633 VOID 01634 NTAPI 01635 LANUnregisterProtocol(VOID) 01636 /* 01637 * FUNCTION: Unregisters this protocol driver with NDIS 01638 * NOTES: Does not care wether we are already registered 01639 */ 01640 { 01641 TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); 01642 01643 if (ProtocolRegistered) { 01644 NDIS_STATUS NdisStatus; 01645 PLIST_ENTRY CurrentEntry; 01646 PLIST_ENTRY NextEntry; 01647 PLAN_ADAPTER Current; 01648 KIRQL OldIrql; 01649 01650 TcpipAcquireSpinLock(&AdapterListLock, &OldIrql); 01651 01652 /* Search the list and remove every adapter we find */ 01653 CurrentEntry = AdapterListHead.Flink; 01654 while (CurrentEntry != &AdapterListHead) { 01655 NextEntry = CurrentEntry->Flink; 01656 Current = CONTAINING_RECORD(CurrentEntry, LAN_ADAPTER, ListEntry); 01657 /* Unregister it */ 01658 LANUnregisterAdapter(Current); 01659 CurrentEntry = NextEntry; 01660 } 01661 01662 TcpipReleaseSpinLock(&AdapterListLock, OldIrql); 01663 01664 NdisDeregisterProtocol(&NdisStatus, NdisProtocolHandle); 01665 ProtocolRegistered = FALSE; 01666 } 01667 } 01668 01669 VOID 01670 NTAPI 01671 ProtocolUnbindAdapter( 01672 PNDIS_STATUS Status, 01673 NDIS_HANDLE ProtocolBindingContext, 01674 NDIS_HANDLE UnbindContext) 01675 { 01676 /* We don't pend any unbinding so we can just ignore UnbindContext */ 01677 *Status = LANUnregisterAdapter((PLAN_ADAPTER)ProtocolBindingContext); 01678 } 01679 01680 NTSTATUS LANRegisterProtocol( 01681 PNDIS_STRING Name) 01682 /* 01683 * FUNCTION: Registers this protocol driver with NDIS 01684 * ARGUMENTS: 01685 * Name = Name of this protocol driver 01686 * RETURNS: 01687 * Status of operation 01688 */ 01689 { 01690 NDIS_STATUS NdisStatus; 01691 NDIS_PROTOCOL_CHARACTERISTICS ProtChars; 01692 01693 TI_DbgPrint(DEBUG_DATALINK, ("Called.\n")); 01694 01695 InitializeListHead(&AdapterListHead); 01696 KeInitializeSpinLock(&AdapterListLock); 01697 01698 /* Set up protocol characteristics */ 01699 RtlZeroMemory(&ProtChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS)); 01700 ProtChars.MajorNdisVersion = NDIS_VERSION_MAJOR; 01701 ProtChars.MinorNdisVersion = NDIS_VERSION_MINOR; 01702 ProtChars.Name.Length = Name->Length; 01703 ProtChars.Name.Buffer = Name->Buffer; 01704 ProtChars.Name.MaximumLength = Name->MaximumLength; 01705 ProtChars.OpenAdapterCompleteHandler = ProtocolOpenAdapterComplete; 01706 ProtChars.CloseAdapterCompleteHandler = ProtocolCloseAdapterComplete; 01707 ProtChars.ResetCompleteHandler = ProtocolResetComplete; 01708 ProtChars.RequestCompleteHandler = ProtocolRequestComplete; 01709 ProtChars.SendCompleteHandler = ProtocolSendComplete; 01710 ProtChars.TransferDataCompleteHandler = ProtocolTransferDataComplete; 01711 ProtChars.ReceivePacketHandler = ProtocolReceivePacket; 01712 ProtChars.ReceiveHandler = ProtocolReceive; 01713 ProtChars.ReceiveCompleteHandler = ProtocolReceiveComplete; 01714 ProtChars.StatusHandler = ProtocolStatus; 01715 ProtChars.StatusCompleteHandler = ProtocolStatusComplete; 01716 ProtChars.BindAdapterHandler = ProtocolBindAdapter; 01717 ProtChars.PnPEventHandler = ProtocolPnPEvent; 01718 ProtChars.UnbindAdapterHandler = ProtocolUnbindAdapter; 01719 ProtChars.UnloadHandler = LANUnregisterProtocol; 01720 01721 /* Try to register protocol */ 01722 NdisRegisterProtocol(&NdisStatus, 01723 &NdisProtocolHandle, 01724 &ProtChars, 01725 sizeof(NDIS_PROTOCOL_CHARACTERISTICS)); 01726 if (NdisStatus != NDIS_STATUS_SUCCESS) 01727 { 01728 TI_DbgPrint(DEBUG_DATALINK, ("NdisRegisterProtocol failed, status 0x%x\n", NdisStatus)); 01729 return (NTSTATUS)NdisStatus; 01730 } 01731 01732 ProtocolRegistered = TRUE; 01733 01734 return STATUS_SUCCESS; 01735 } 01736 01737 /* EOF */ Generated on Sun May 27 2012 04:28:04 for ReactOS by
1.7.6.1
|