More work on winsock stack (ping is now working)
[reactos.git] / reactos / drivers / net / tcpip / transport / datagram / datagram.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: transport/datagram/datagram.c
5 * PURPOSE: Routines for sending and receiving datagrams
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * REVISIONS:
8 * CSH 01/08-2000 Created
9 */
10 #include <tcpip.h>
11 #include <datagram.h>
12 #include <routines.h>
13 #include <transmit.h>
14 #include <address.h>
15 #include <route.h>
16 #include <pool.h>
17
18
19 /* Pending request queue */
20 LIST_ENTRY DGPendingListHead;
21 KSPIN_LOCK DGPendingListLock;
22 /* Work queue item for pending requests */
23 WORK_QUEUE_ITEM DGWorkItem;
24
25
26 VOID DatagramWorker(
27 PVOID Context)
28 /*
29 * FUNCTION: Handles pending requests
30 * ARGUMENTS:
31 * Context = Pointer to context information (unused)
32 * NOTES:
33 * This routine is called after the driver has run out of resources.
34 * It processes send requests or shedules them to be processed
35 */
36 {
37 PLIST_ENTRY CurrentADFEntry;
38 PLIST_ENTRY CurrentSREntry;
39 PADDRESS_FILE CurrentADF;
40 PDATAGRAM_SEND_REQUEST CurrentSR;
41 KIRQL OldIrql1;
42 KIRQL OldIrql2;
43
44 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
45
46 KeAcquireSpinLock(&DGPendingListLock, &OldIrql1);
47
48 CurrentADFEntry = DGPendingListHead.Flink;
49 while (CurrentADFEntry != &DGPendingListHead) {
50 RemoveEntryList(CurrentADFEntry);
51 CurrentADF = CONTAINING_RECORD(CurrentADFEntry,
52 ADDRESS_FILE,
53 ListEntry);
54
55 KeAcquireSpinLock(&CurrentADF->Lock, &OldIrql2);
56
57 if (AF_IS_BUSY(CurrentADF)) {
58 /* The send worker function is already running so we just
59 set the pending send flag on the address file object */
60
61 AF_SET_PENDING(CurrentADF, AFF_SEND);
62 KeReleaseSpinLock(&CurrentADF->Lock, OldIrql2);
63 } else {
64 if (!IsListEmpty(&CurrentADF->TransmitQueue)) {
65 /* The transmit queue is not empty. Dequeue a send
66 request and process it */
67
68 CurrentSREntry = RemoveHeadList(&CurrentADF->TransmitQueue);
69 CurrentSR = CONTAINING_RECORD(CurrentADFEntry,
70 DATAGRAM_SEND_REQUEST,
71 ListEntry);
72
73 KeReleaseSpinLock(&CurrentADF->Lock, OldIrql2);
74
75 DGSend(CurrentADF, CurrentSR);
76 } else
77 KeReleaseSpinLock(&CurrentADF->Lock, OldIrql2);
78 }
79 CurrentADFEntry = CurrentADFEntry->Flink;
80 }
81
82 KeReleaseSpinLock(&DGPendingListLock, OldIrql1);
83
84 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
85 }
86
87
88 VOID SendDatagramComplete(
89 PVOID Context,
90 PNDIS_PACKET Packet,
91 NDIS_STATUS NdisStatus)
92 /*
93 * FUNCTION: Datagram transmit completion handler
94 * ARGUMENTS:
95 * Context = Pointer to context infomation (DATAGRAM_SEND_REQUEST)
96 * Packet = Pointer to NDIS packet
97 * NdisStatus = Status of transmit operation
98 * NOTES:
99 * This routine is called by IP when a datagram send completes.
100 * We shedule the out-of-resource worker function if there
101 * are pending address files in the queue
102 */
103 {
104 KIRQL OldIrql;
105 ULONG BytesSent;
106 PVOID CompleteContext;
107 PNDIS_BUFFER NdisBuffer;
108 PDATAGRAM_SEND_REQUEST SendRequest;
109 DATAGRAM_COMPLETION_ROUTINE Complete;
110 BOOLEAN QueueWorkItem;
111
112 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
113
114 SendRequest = (PDATAGRAM_SEND_REQUEST)Context;
115 Complete = SendRequest->Complete;
116 CompleteContext = SendRequest->Context;
117 BytesSent = SendRequest->BufferSize;
118
119 /* Remove data buffer before releasing memory for packet buffers */
120 NdisQueryPacket(Packet, NULL, NULL, &NdisBuffer, NULL);
121 NdisUnchainBufferAtBack(Packet, &NdisBuffer);
122 FreeNdisPacket(Packet);
123 DereferenceObject(SendRequest->RemoteAddress);
124 PoolFreeBuffer(SendRequest);
125
126 /* If there are pending send requests, shedule worker function */
127 KeAcquireSpinLock(&DGPendingListLock, &OldIrql);
128 QueueWorkItem = (!IsListEmpty(&DGPendingListHead));
129 KeReleaseSpinLock(&DGPendingListLock, OldIrql);
130 if (QueueWorkItem)
131 ExQueueWorkItem(&DGWorkItem, CriticalWorkQueue);
132
133 /* Call completion routine for send request */
134 (*Complete)(CompleteContext, NdisStatus, BytesSent);
135
136 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
137 }
138
139
140 VOID DGSend(
141 PVOID Context,
142 PDATAGRAM_SEND_REQUEST SendRequest)
143 /*
144 * FUNCTION: Sends a datagram to IP layer
145 * ARGUMENTS:
146 * Context = Pointer to context information (ADDRESS_FILE)
147 * SendRequest = Pointer to send request
148 */
149 {
150 KIRQL OldIrql;
151 NTSTATUS Status;
152 USHORT LocalPort;
153 PIP_PACKET IPPacket;
154 PROUTE_CACHE_NODE RCN;
155 PLIST_ENTRY CurrentEntry;
156 PADDRESS_FILE AddrFile = Context;
157 PADDRESS_ENTRY ADE;
158
159 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
160
161 /* Get the information we need from the address file
162 now so we minimize the time we hold the spin lock */
163 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
164 LocalPort = AddrFile->Port;
165 ADE = AddrFile->ADE;
166 ReferenceObject(ADE);
167 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
168
169 /* Loop until there are no more send requests in the
170 transmit queue or until we run out of resources */
171 for (;;) {
172 Status = SendRequest->Build(SendRequest, ADE->Address, LocalPort, &IPPacket);
173 if (!NT_SUCCESS(Status)) {
174 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
175 /* An error occurred, enqueue the send request again and return */
176 InsertTailList(&AddrFile->TransmitQueue, &SendRequest->ListEntry);
177 DereferenceObject(ADE);
178 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
179
180 TI_DbgPrint(MIN_TRACE, ("Leaving (insufficient resources).\n"));
181 return;
182 }
183
184 /* Get a route to the destination address */
185 if (RouteGetRouteToDestination(SendRequest->RemoteAddress, ADE->NTE, &RCN) == IP_SUCCESS) {
186 /* Set completion routine and send the packet */
187 PC(IPPacket->NdisPacket)->Complete = SendDatagramComplete;
188 PC(IPPacket->NdisPacket)->Context = SendRequest;
189 if (IPSendDatagram(IPPacket, RCN) != STATUS_SUCCESS)
190 SendDatagramComplete(SendRequest,
191 IPPacket->NdisPacket,
192 NDIS_STATUS_REQUEST_ABORTED);
193 /* We're done with the RCN */
194 DereferenceObject(RCN);
195 } else {
196 /* No route to destination */
197 /* FIXME: Which error code should we use here? */
198 TI_DbgPrint(MIN_TRACE, ("No route to destination address (0x%X).\n",
199 SendRequest->RemoteAddress->Address.IPv4Address));
200 SendDatagramComplete(SendRequest,
201 IPPacket->NdisPacket,
202 NDIS_STATUS_REQUEST_ABORTED);
203 }
204
205 PoolFreeBuffer(IPPacket);
206
207 /* Check transmit queue for more to send */
208
209 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
210
211 if (!IsListEmpty(&AddrFile->TransmitQueue)) {
212 /* Transmit queue is not empty, process one more request */
213 CurrentEntry = RemoveHeadList(&AddrFile->TransmitQueue);
214 SendRequest = CONTAINING_RECORD(CurrentEntry, DATAGRAM_SEND_REQUEST, ListEntry);
215
216 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
217 } else {
218 /* Transmit queue is empty */
219 AF_CLR_PENDING(AddrFile, AFF_SEND);
220 DereferenceObject(ADE);
221 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
222
223 TI_DbgPrint(MAX_TRACE, ("Leaving (empty queue).\n"));
224 return;
225 }
226 }
227 }
228
229
230 VOID DGDeliverData(
231 PADDRESS_FILE AddrFile,
232 PIP_ADDRESS Address,
233 PIP_PACKET IPPacket,
234 UINT DataSize)
235 /*
236 * FUNCTION: Delivers datagram data to a user
237 * ARGUMENTS:
238 * AddrFile = Address file to deliver data to
239 * Address = Remote address the packet came from
240 * IPPacket = Pointer to IP packet to deliver
241 * DataSize = Number of bytes in data area
242 * (incl. IP header for raw IP file objects)
243 * NOTES:
244 * If there is a receive request, then we copy the data to the
245 * buffer supplied by the user and complete the receive request.
246 * If no suitable receive request exists, then we call the event
247 * handler if it exists, otherwise we drop the packet.
248 */
249 {
250 KIRQL OldIrql;
251 PTDI_IND_RECEIVE_DATAGRAM ReceiveHandler;
252 PVOID HandlerContext;
253 LONG AddressLength;
254 PVOID SourceAddress;
255 ULONG BytesTaken;
256 NTSTATUS Status;
257 PVOID DataBuffer;
258
259 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
260
261 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
262
263 if (AddrFile->Protocol == IPPROTO_UDP) {
264 DataBuffer = IPPacket->Data;
265 } else {
266 /* Give client the IP header too if it is a raw IP file object */
267 DataBuffer = IPPacket->Header;
268 }
269
270 if (!IsListEmpty(&AddrFile->ReceiveQueue)) {
271 PLIST_ENTRY CurrentEntry;
272 PDATAGRAM_RECEIVE_REQUEST Current;
273 BOOLEAN Found;
274
275 TI_DbgPrint(MAX_TRACE, ("There is a receive request.\n"));
276
277 /* Search receive request list to find a match */
278 Found = FALSE;
279 CurrentEntry = AddrFile->ReceiveQueue.Flink;
280 while ((CurrentEntry != &AddrFile->ReceiveQueue) && (!Found)) {
281 Current = CONTAINING_RECORD(CurrentEntry, DATAGRAM_RECEIVE_REQUEST, ListEntry);
282 if (!Current->RemoteAddress)
283 Found = TRUE;
284 else if (AddrIsEqual(Address, Current->RemoteAddress))
285 Found = TRUE;
286
287 if (Found) {
288 /* FIXME: Maybe we should check if the buffer of this
289 receive request is large enough and if not, search
290 for another */
291
292 /* Remove the request from the queue */
293 RemoveEntryList(&Current->ListEntry);
294 AddrFile->RefCount--;
295 break;
296 }
297 CurrentEntry = CurrentEntry->Flink;
298 }
299
300 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
301
302 if (Found) {
303 TI_DbgPrint(MAX_TRACE, ("Suitable receive request found.\n"));
304
305 /* Copy the data into buffer provided by the user */
306 CopyBufferToBufferChain(Current->Buffer,
307 0,
308 DataBuffer,
309 DataSize);
310
311 /* Complete the receive request */
312 (*Current->Complete)(Current->Context, STATUS_SUCCESS, DataSize);
313
314 /* Finally free the receive request */
315 if (Current->RemoteAddress)
316 PoolFreeBuffer(Current->RemoteAddress);
317 PoolFreeBuffer(Current);
318 }
319 } else if (AddrFile->RegisteredReceiveDatagramHandler) {
320 TI_DbgPrint(MAX_TRACE, ("Calling receive event handler.\n"));
321
322 ReceiveHandler = AddrFile->ReceiveDatagramHandler;
323 HandlerContext = AddrFile->ReceiveDatagramHandlerContext;
324
325 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
326
327 if (Address->Type == IP_ADDRESS_V4) {
328 AddressLength = sizeof(IPv4_RAW_ADDRESS);
329 SourceAddress = &Address->Address.IPv4Address;
330 } else /* (Address->Type == IP_ADDRESS_V6) */ {
331 AddressLength = sizeof(IPv6_RAW_ADDRESS);
332 SourceAddress = Address->Address.IPv6Address;
333 }
334
335 Status = (*ReceiveHandler)(HandlerContext,
336 AddressLength,
337 SourceAddress,
338 0,
339 NULL,
340 TDI_RECEIVE_ENTIRE_MESSAGE,
341 DataSize,
342 DataSize,
343 &BytesTaken,
344 DataBuffer,
345 NULL);
346 } else {
347 TI_DbgPrint(MAX_TRACE, ("Discarding datagram.\n"));
348 }
349
350 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
351 }
352
353
354 VOID DGCancelSendRequest(
355 PADDRESS_FILE AddrFile,
356 PVOID Context)
357 /*
358 * FUNCTION: Cancels a datagram send request
359 * ARGUMENTS:
360 * AddrFile = Pointer to address file of the request
361 * Context = Pointer to context information for completion handler
362 */
363 {
364 KIRQL OldIrql;
365 PLIST_ENTRY CurrentEntry;
366 PDATAGRAM_SEND_REQUEST Current = NULL;
367 BOOLEAN Found = FALSE;
368
369 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
370
371 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
372
373 /* Search the request list for the specified request and remove it */
374 CurrentEntry = AddrFile->TransmitQueue.Flink;
375 while ((CurrentEntry != &AddrFile->TransmitQueue) && (!Found)) {
376 Current = CONTAINING_RECORD(CurrentEntry, DATAGRAM_SEND_REQUEST, ListEntry);
377 if (Context == Current->Context) {
378 /* We've found the request, now remove it from the queue */
379 RemoveEntryList(CurrentEntry);
380 AddrFile->RefCount--;
381 Found = TRUE;
382 break;
383 }
384 CurrentEntry = CurrentEntry->Flink;
385 }
386
387 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
388
389 if (Found) {
390 /* Complete the request and free its resources */
391 (*Current->Complete)(Current->Context, STATUS_CANCELLED, 0);
392 PoolFreeBuffer(Current->RemoteAddress);
393 PoolFreeBuffer(Current);
394 } else {
395 TI_DbgPrint(MID_TRACE, ("Cannot find send request.\n"));
396 }
397 }
398
399
400 VOID DGCancelReceiveRequest(
401 PADDRESS_FILE AddrFile,
402 PVOID Context)
403 /*
404 * FUNCTION: Cancels a datagram receive request
405 * ARGUMENTS:
406 * AddrFile = Pointer to address file of the request
407 * Context = Pointer to context information for completion handler
408 */
409 {
410 KIRQL OldIrql;
411 PLIST_ENTRY CurrentEntry;
412 PDATAGRAM_RECEIVE_REQUEST Current = NULL;
413 BOOLEAN Found = FALSE;
414
415 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
416
417 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
418
419 /* Search the request list for the specified request and remove it */
420 CurrentEntry = AddrFile->ReceiveQueue.Flink;
421 while ((CurrentEntry != &AddrFile->ReceiveQueue) && (!Found)) {
422 Current = CONTAINING_RECORD(CurrentEntry, DATAGRAM_RECEIVE_REQUEST, ListEntry);
423 if (Context == Current->Context) {
424 /* We've found the request, now remove it from the queue */
425 RemoveEntryList(CurrentEntry);
426 AddrFile->RefCount--;
427 Found = TRUE;
428 break;
429 }
430 CurrentEntry = CurrentEntry->Flink;
431 }
432
433 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
434
435 if (Found) {
436 /* Complete the request and free its resources */
437 (*Current->Complete)(Current->Context, STATUS_CANCELLED, 0);
438 /* Remote address can be NULL if the caller wants to receive
439 packets sent from any address */
440 if (Current->RemoteAddress)
441 PoolFreeBuffer(Current->RemoteAddress);
442 PoolFreeBuffer(Current);
443 } else {
444 TI_DbgPrint(MID_TRACE, ("Cannot find receive request.\n"));
445 }
446 }
447
448
449 NTSTATUS DGSendDatagram(
450 PTDI_REQUEST Request,
451 PTDI_CONNECTION_INFORMATION ConnInfo,
452 PNDIS_BUFFER Buffer,
453 ULONG DataSize,
454 DATAGRAM_BUILD_ROUTINE Build)
455 /*
456 * FUNCTION: Sends a datagram to a remote address
457 * ARGUMENTS:
458 * Request = Pointer to TDI request
459 * ConnInfo = Pointer to connection information
460 * Buffer = Pointer to NDIS buffer with data
461 * DataSize = Size in bytes of data to be sent
462 * Build = Pointer to datagram build routine
463 * RETURNS:
464 * Status of operation
465 */
466 {
467 PADDRESS_FILE AddrFile;
468 KIRQL OldIrql;
469 NTSTATUS Status;
470 PDATAGRAM_SEND_REQUEST SendRequest = NULL;
471
472 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
473
474 AddrFile = Request->Handle.AddressHandle;
475
476 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
477
478 if (AF_IS_VALID(AddrFile)) {
479 SendRequest = PoolAllocateBuffer(sizeof(DATAGRAM_SEND_REQUEST));
480 if (SendRequest) {
481 /* Initialize a send request */
482 Status = AddrGetAddress(ConnInfo->RemoteAddress,
483 &SendRequest->RemoteAddress, &SendRequest->RemotePort,
484 &AddrFile->AddrCache);
485 if (NT_SUCCESS(Status)) {
486 SendRequest->Buffer = Buffer;
487 SendRequest->BufferSize = DataSize;
488 SendRequest->Complete = Request->RequestNotifyObject;
489 SendRequest->Context = Request->RequestContext;
490 SendRequest->Build = Build;
491
492 if (AF_IS_BUSY(AddrFile)) {
493 /* Queue send request on the transmit queue */
494 InsertTailList(&AddrFile->TransmitQueue, &SendRequest->ListEntry);
495
496 /* Reference address file and set pending send request flag */
497 AddrFile->RefCount++;
498 AF_SET_PENDING(AddrFile, AFF_SEND);
499
500 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
501
502 TI_DbgPrint(MAX_TRACE, ("Leaving (queued).\n"));
503 } else {
504 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
505
506 /* Send the datagram */
507 DGSend(AddrFile, SendRequest);
508
509 TI_DbgPrint(MAX_TRACE, ("Leaving (pending).\n"));
510 }
511 return STATUS_PENDING;
512 }
513 } else
514 Status = STATUS_INSUFFICIENT_RESOURCES;
515 } else
516 Status = STATUS_ADDRESS_CLOSED;
517
518 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
519
520 TI_DbgPrint(MAX_TRACE, ("Leaving. Status (0x%X)\n", Status));
521
522 return Status;
523 }
524
525
526 NTSTATUS DGReceiveDatagram(
527 PTDI_REQUEST Request,
528 PTDI_CONNECTION_INFORMATION ConnInfo,
529 PNDIS_BUFFER Buffer,
530 ULONG ReceiveLength,
531 ULONG ReceiveFlags,
532 PTDI_CONNECTION_INFORMATION ReturnInfo,
533 PULONG BytesReceived)
534 /*
535 * FUNCTION: Attempts to receive a datagram from a remote address
536 * ARGUMENTS:
537 * Request = Pointer to TDI request
538 * ConnInfo = Pointer to connection information
539 * Buffer = Pointer to NDIS buffer chain to store received data
540 * ReceiveLength = Maximum size to use of buffer (0 if all can be used)
541 * ReceiveFlags = Receive flags (None, Normal, Peek)
542 * ReturnInfo = Pointer to structure for return information
543 * BytesReceive = Pointer to structure for number of bytes received
544 * RETURNS:
545 * Status of operation
546 * NOTES:
547 * This is the high level interface for receiving datagrams
548 */
549 {
550 PADDRESS_FILE AddrFile;
551 KIRQL OldIrql;
552 NTSTATUS Status;
553 PDATAGRAM_RECEIVE_REQUEST ReceiveRequest;
554
555 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
556
557 AddrFile = Request->Handle.AddressHandle;
558
559 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
560
561 if (AF_IS_VALID(AddrFile)) {
562 ReceiveRequest = PoolAllocateBuffer(sizeof(DATAGRAM_RECEIVE_REQUEST));
563 if (ReceiveRequest) {
564 /* Initialize a receive request */
565
566 /* Extract the remote address filter from the request (if any) */
567 if (((ConnInfo->RemoteAddressLength != 0)) && (ConnInfo->RemoteAddress)) {
568 Status = AddrGetAddress(ConnInfo->RemoteAddress,
569 &ReceiveRequest->RemoteAddress,
570 &ReceiveRequest->RemotePort,
571 &AddrFile->AddrCache);
572 if (!NT_SUCCESS(Status)) {
573 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
574 PoolFreeBuffer(ReceiveRequest);
575 return Status;
576 }
577 } else {
578 ReceiveRequest->RemotePort = 0;
579 ReceiveRequest->RemoteAddress = NULL;
580 }
581 ReceiveRequest->ReturnInfo = ReturnInfo;
582 ReceiveRequest->Buffer = Buffer;
583 /* If ReceiveLength is 0, the whole buffer is available to us */
584 ReceiveRequest->BufferSize = (ReceiveLength == 0) ?
585 MmGetMdlByteCount(Buffer) : ReceiveLength;
586 ReceiveRequest->Complete = Request->RequestNotifyObject;
587 ReceiveRequest->Context = Request->RequestContext;
588
589 /* Queue receive request */
590 InsertTailList(&AddrFile->ReceiveQueue, &ReceiveRequest->ListEntry);
591
592 /* Reference address file and set pending receive request flag */
593 AddrFile->RefCount++;
594 AF_SET_PENDING(AddrFile, AFF_RECEIVE);
595
596 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
597
598 TI_DbgPrint(MAX_TRACE, ("Leaving (pending).\n"));
599
600 return STATUS_PENDING;
601 } else
602 Status = STATUS_INSUFFICIENT_RESOURCES;
603 } else
604 Status = STATUS_INVALID_ADDRESS;
605
606 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
607
608 TI_DbgPrint(MAX_TRACE, ("Leaving with errors (0x%X).\n", Status));
609
610 return Status;
611 }
612
613
614 NTSTATUS DGStartup(
615 VOID)
616 /*
617 * FUNCTION: Initializes the datagram subsystem
618 * RETURNS:
619 * Status of operation
620 */
621 {
622 InitializeListHead(&DGPendingListHead);
623
624 KeInitializeSpinLock(&DGPendingListLock);
625
626 ExInitializeWorkItem(&DGWorkItem, DatagramWorker, NULL);
627
628 return STATUS_SUCCESS;
629 }
630
631
632 NTSTATUS DGShutdown(
633 VOID)
634 /*
635 * FUNCTION: Shuts down the datagram subsystem
636 * RETURNS:
637 * Status of operation
638 */
639 {
640 return STATUS_SUCCESS;
641 }
642
643
644 /* EOF */