e1d6cbd7dfd18dd81eb855f63e88b6c7f7b8f77b
[reactos.git] / reactos / ntoskrnl / lpc / send.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/lpc/send.c
5 * PURPOSE: Local Procedure Call: Sending (Requests)
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* PUBLIC FUNCTIONS **********************************************************/
16
17 /*
18 * @implemented
19 */
20 NTSTATUS
21 NTAPI
22 LpcRequestPort(IN PVOID PortObject,
23 IN PPORT_MESSAGE LpcMessage)
24 {
25 PLPCP_PORT_OBJECT Port = PortObject, QueuePort, ConnectionPort = NULL;
26 ULONG MessageType;
27 PLPCP_MESSAGE Message;
28 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
29 PAGED_CODE();
30 LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", Port, LpcMessage);
31
32 /* Check if this is a non-datagram message */
33 if (LpcMessage->u2.s2.Type)
34 {
35 /* Get the message type */
36 MessageType = LpcpGetMessageType(LpcMessage);
37
38 /* Validate it */
39 if ((MessageType < LPC_DATAGRAM) || (MessageType > LPC_CLIENT_DIED))
40 {
41 /* Fail */
42 return STATUS_INVALID_PARAMETER;
43 }
44
45 /* Mark this as a kernel-mode message only if we really came from it */
46 if ((PreviousMode == KernelMode) &&
47 (LpcMessage->u2.s2.Type & LPC_KERNELMODE_MESSAGE))
48 {
49 /* We did, this is a kernel mode message */
50 MessageType |= LPC_KERNELMODE_MESSAGE;
51 }
52 }
53 else
54 {
55 /* This is a datagram */
56 MessageType = LPC_DATAGRAM;
57 }
58
59 /* Can't have data information on this type of call */
60 if (LpcMessage->u2.s2.DataInfoOffset) return STATUS_INVALID_PARAMETER;
61
62 /* Validate message sizes */
63 if (((ULONG)LpcMessage->u1.s1.TotalLength > Port->MaxMessageLength) ||
64 ((ULONG)LpcMessage->u1.s1.TotalLength <= (ULONG)LpcMessage->u1.s1.DataLength))
65 {
66 /* Fail */
67 return STATUS_PORT_MESSAGE_TOO_LONG;
68 }
69
70 /* Allocate a new message */
71 Message = LpcpAllocateFromPortZone();
72 if (!Message) return STATUS_NO_MEMORY;
73
74 /* Clear the context */
75 Message->RepliedToThread = NULL;
76 Message->PortContext = NULL;
77
78 /* Copy the message */
79 LpcpMoveMessage(&Message->Request,
80 LpcMessage,
81 LpcMessage + 1,
82 MessageType,
83 &PsGetCurrentThread()->Cid);
84
85 /* Acquire the LPC lock */
86 KeAcquireGuardedMutex(&LpcpLock);
87
88 /* Check if this is anything but a connection port */
89 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
90 {
91 /* The queue port is the connected port */
92 QueuePort = Port->ConnectedPort;
93 if (QueuePort)
94 {
95 /* Check if this is a client port */
96 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
97 {
98 /* Then copy the context */
99 Message->PortContext = QueuePort->PortContext;
100 ConnectionPort = QueuePort = Port->ConnectionPort;
101 if (!ConnectionPort)
102 {
103 /* Fail */
104 LpcpFreeToPortZone(Message, 3);
105 return STATUS_PORT_DISCONNECTED;
106 }
107 }
108 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT)
109 {
110 /* Any other kind of port, use the connection port */
111 ConnectionPort = QueuePort = Port->ConnectionPort;
112 if (!ConnectionPort)
113 {
114 /* Fail */
115 LpcpFreeToPortZone(Message, 3);
116 return STATUS_PORT_DISCONNECTED;
117 }
118 }
119
120 /* If we have a connection port, reference it */
121 if (ConnectionPort) ObReferenceObject(ConnectionPort);
122 }
123 }
124 else
125 {
126 /* For connection ports, use the port itself */
127 QueuePort = PortObject;
128 }
129
130 /* Make sure we have a port */
131 if (QueuePort)
132 {
133 /* Generate the Message ID and set it */
134 Message->Request.MessageId = LpcpNextMessageId++;
135 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
136 Message->Request.CallbackId = 0;
137
138 /* No Message ID for the thread */
139 PsGetCurrentThread()->LpcReplyMessageId = 0;
140
141 /* Insert the message in our chain */
142 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
143
144 /* Release the lock and release the semaphore */
145 KeEnterCriticalRegion();
146 KeReleaseGuardedMutex(&LpcpLock);
147 LpcpCompleteWait(QueuePort->MsgQueue.Semaphore);
148
149 /* If this is a waitable port, wake it up */
150 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
151 {
152 /* Wake it */
153 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
154 }
155
156 /* We're done */
157 KeLeaveCriticalRegion();
158 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
159 LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", QueuePort, Message);
160 return STATUS_SUCCESS;
161 }
162
163 /* If we got here, then free the message and fail */
164 LpcpFreeToPortZone(Message, 3);
165 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
166 return STATUS_PORT_DISCONNECTED;
167 }
168
169 /*
170 * @implemented
171 */
172 NTSTATUS
173 NTAPI
174 LpcRequestWaitReplyPort(IN PVOID PortObject,
175 IN PPORT_MESSAGE LpcRequest,
176 OUT PPORT_MESSAGE LpcReply)
177 {
178 PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL;
179 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
180 NTSTATUS Status = STATUS_SUCCESS;
181 PLPCP_MESSAGE Message;
182 PETHREAD Thread = PsGetCurrentThread();
183 BOOLEAN Callback;
184 PKSEMAPHORE Semaphore;
185 ULONG MessageType;
186 PAGED_CODE();
187
188 Port = (PLPCP_PORT_OBJECT)PortObject;
189
190 LPCTRACE(LPC_SEND_DEBUG,
191 "Port: %p. Messages: %p/%p. Type: %lx\n",
192 Port,
193 LpcRequest,
194 LpcReply,
195 LpcpGetMessageType(LpcRequest));
196
197 /* Check if the thread is dying */
198 if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING;
199
200 /* Check if this is an LPC Request */
201 if (LpcpGetMessageType(LpcRequest) == LPC_REQUEST)
202 {
203 /* Then it's a callback */
204 Callback = TRUE;
205 }
206 else
207 {
208 /* This is a kernel-mode message without a callback */
209 LpcRequest->u2.s2.Type |= LPC_REQUEST;
210 Callback = FALSE;
211 }
212
213 /* Get the message type */
214 MessageType = LpcRequest->u2.s2.Type;
215
216 /* Validate the length */
217 if (((ULONG)LpcRequest->u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
218 (ULONG)LpcRequest->u1.s1.TotalLength)
219 {
220 /* Fail */
221 return STATUS_INVALID_PARAMETER;
222 }
223
224 /* Validate the message length */
225 if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) ||
226 ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength))
227 {
228 /* Fail */
229 return STATUS_PORT_MESSAGE_TOO_LONG;
230 }
231
232 /* Allocate a message from the port zone */
233 Message = LpcpAllocateFromPortZone();
234 if (!Message)
235 {
236 /* Fail if we couldn't allocate a message */
237 return STATUS_NO_MEMORY;
238 }
239
240 /* Check if this is a callback */
241 if (Callback)
242 {
243 /* FIXME: TODO */
244 Semaphore = NULL; // we'd use the Thread Semaphore here
245 ASSERT(FALSE);
246 }
247 else
248 {
249 /* No callback, just copy the message */
250 LpcpMoveMessage(&Message->Request,
251 LpcRequest,
252 LpcRequest + 1,
253 MessageType,
254 &Thread->Cid);
255
256 /* Acquire the LPC lock */
257 KeAcquireGuardedMutex(&LpcpLock);
258
259 /* Right now clear the port context */
260 Message->PortContext = NULL;
261
262 /* Check if this is a not connection port */
263 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
264 {
265 /* We want the connected port */
266 QueuePort = Port->ConnectedPort;
267 if (!QueuePort)
268 {
269 /* We have no connected port, fail */
270 LpcpFreeToPortZone(Message, 3);
271 return STATUS_PORT_DISCONNECTED;
272 }
273
274 /* This will be the rundown port */
275 ReplyPort = QueuePort;
276
277 /* Check if this is a communication port */
278 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
279 {
280 /* Copy the port context and use the connection port */
281 Message->PortContext = QueuePort->PortContext;
282 ConnectionPort = QueuePort = Port->ConnectionPort;
283 if (!ConnectionPort)
284 {
285 /* Fail */
286 LpcpFreeToPortZone(Message, 3);
287 return STATUS_PORT_DISCONNECTED;
288 }
289 }
290 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) !=
291 LPCP_COMMUNICATION_PORT)
292 {
293 /* Use the connection port for anything but communication ports */
294 ConnectionPort = QueuePort = Port->ConnectionPort;
295 if (!ConnectionPort)
296 {
297 /* Fail */
298 LpcpFreeToPortZone(Message, 3);
299 return STATUS_PORT_DISCONNECTED;
300 }
301 }
302
303 /* Reference the connection port if it exists */
304 if (ConnectionPort) ObReferenceObject(ConnectionPort);
305 }
306 else
307 {
308 /* Otherwise, for a connection port, use the same port object */
309 QueuePort = ReplyPort = Port;
310 }
311
312 /* No reply thread */
313 Message->RepliedToThread = NULL;
314 Message->SenderPort = Port;
315
316 /* Generate the Message ID and set it */
317 Message->Request.MessageId = LpcpNextMessageId++;
318 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
319 Message->Request.CallbackId = 0;
320
321 /* Set the message ID for our thread now */
322 Thread->LpcReplyMessageId = Message->Request.MessageId;
323 Thread->LpcReplyMessage = NULL;
324
325 /* Insert the message in our chain */
326 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
327 InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain);
328 LpcpSetPortToThread(Thread, Port);
329
330 /* Release the lock and get the semaphore we'll use later */
331 KeEnterCriticalRegion();
332 KeReleaseGuardedMutex(&LpcpLock);
333 Semaphore = QueuePort->MsgQueue.Semaphore;
334
335 /* If this is a waitable port, wake it up */
336 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
337 {
338 /* Wake it */
339 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
340 }
341 }
342
343 /* Now release the semaphore */
344 LpcpCompleteWait(Semaphore);
345 KeLeaveCriticalRegion();
346
347 /* And let's wait for the reply */
348 LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode);
349
350 /* Acquire the LPC lock */
351 KeAcquireGuardedMutex(&LpcpLock);
352
353 /* Get the LPC Message and clear our thread's reply data */
354 Message = LpcpGetMessageFromThread(Thread);
355 Thread->LpcReplyMessage = NULL;
356 Thread->LpcReplyMessageId = 0;
357
358 /* Check if we have anything on the reply chain*/
359 if (!IsListEmpty(&Thread->LpcReplyChain))
360 {
361 /* Remove this thread and reinitialize the list */
362 RemoveEntryList(&Thread->LpcReplyChain);
363 InitializeListHead(&Thread->LpcReplyChain);
364 }
365
366 /* Release the lock */
367 KeReleaseGuardedMutex(&LpcpLock);
368
369 /* Check if we got a reply */
370 if (Status == STATUS_SUCCESS)
371 {
372 /* Check if we have a valid message */
373 if (Message)
374 {
375 LPCTRACE(LPC_SEND_DEBUG,
376 "Reply Messages: %p/%p\n",
377 &Message->Request,
378 (&Message->Request) + 1);
379
380 /* Move the message */
381 LpcpMoveMessage(LpcReply,
382 &Message->Request,
383 (&Message->Request) + 1,
384 0,
385 NULL);
386
387 /* Check if this is an LPC request with data information */
388 if ((LpcpGetMessageType(&Message->Request) == LPC_REQUEST) &&
389 (Message->Request.u2.s2.DataInfoOffset))
390 {
391 /* Save the data information */
392 LpcpSaveDataInfoMessage(Port, Message, 0);
393 }
394 else
395 {
396 /* Otherwise, just free it */
397 LpcpFreeToPortZone(Message, 0);
398 }
399 }
400 else
401 {
402 /* We don't have a reply */
403 Status = STATUS_LPC_REPLY_LOST;
404 }
405 }
406 else
407 {
408 /* The wait failed, free the message */
409 if (Message) LpcpFreeToPortZone(Message, 0);
410 }
411
412 /* All done */
413 LPCTRACE(LPC_SEND_DEBUG,
414 "Port: %p. Status: %p\n",
415 Port,
416 Status);
417
418 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
419 return Status;
420 }
421
422 /*
423 * @unimplemented
424 */
425 NTSTATUS
426 NTAPI
427 NtRequestPort(IN HANDLE PortHandle,
428 IN PPORT_MESSAGE LpcMessage)
429 {
430 UNIMPLEMENTED;
431 return STATUS_NOT_IMPLEMENTED;
432 }
433
434 /*
435 * @implemented
436 */
437 NTSTATUS
438 NTAPI
439 NtRequestWaitReplyPort(IN HANDLE PortHandle,
440 IN PPORT_MESSAGE LpcRequest,
441 IN OUT PPORT_MESSAGE LpcReply)
442 {
443 PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL;
444 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
445 NTSTATUS Status;
446 PLPCP_MESSAGE Message;
447 PETHREAD Thread = PsGetCurrentThread();
448 BOOLEAN Callback;
449 PKSEMAPHORE Semaphore;
450 ULONG MessageType;
451 PAGED_CODE();
452 LPCTRACE(LPC_SEND_DEBUG,
453 "Handle: %lx. Messages: %p/%p. Type: %lx\n",
454 PortHandle,
455 LpcRequest,
456 LpcReply,
457 LpcpGetMessageType(LpcRequest));
458
459 /* Check if the thread is dying */
460 if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING;
461
462 /* Check if this is an LPC Request */
463 if (LpcpGetMessageType(LpcRequest) == LPC_REQUEST)
464 {
465 /* Then it's a callback */
466 Callback = TRUE;
467 }
468 else if (LpcpGetMessageType(LpcRequest))
469 {
470 /* This is a not kernel-mode message */
471 return STATUS_INVALID_PARAMETER;
472 }
473 else
474 {
475 /* This is a kernel-mode message without a callback */
476 LpcRequest->u2.s2.Type |= LPC_REQUEST;
477 Callback = FALSE;
478 }
479
480 /* Get the message type */
481 MessageType = LpcRequest->u2.s2.Type;
482
483 /* Validate the length */
484 if (((ULONG)LpcRequest->u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
485 (ULONG)LpcRequest->u1.s1.TotalLength)
486 {
487 /* Fail */
488 return STATUS_INVALID_PARAMETER;
489 }
490
491 /* Reference the object */
492 Status = ObReferenceObjectByHandle(PortHandle,
493 0,
494 LpcPortObjectType,
495 PreviousMode,
496 (PVOID*)&Port,
497 NULL);
498 if (!NT_SUCCESS(Status)) return Status;
499
500 /* Validate the message length */
501 if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) ||
502 ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength))
503 {
504 /* Fail */
505 ObDereferenceObject(Port);
506 return STATUS_PORT_MESSAGE_TOO_LONG;
507 }
508
509 /* Allocate a message from the port zone */
510 Message = LpcpAllocateFromPortZone();
511 if (!Message)
512 {
513 /* Fail if we couldn't allocate a message */
514 ObDereferenceObject(Port);
515 return STATUS_NO_MEMORY;
516 }
517
518 /* Check if this is a callback */
519 if (Callback)
520 {
521 /* FIXME: TODO */
522 Semaphore = NULL; // we'd use the Thread Semaphore here
523 ASSERT(FALSE);
524 }
525 else
526 {
527 /* No callback, just copy the message */
528 _SEH_TRY
529 {
530 LpcpMoveMessage(&Message->Request,
531 LpcRequest,
532 LpcRequest + 1,
533 MessageType,
534 &Thread->Cid);
535 }
536 _SEH_HANDLE
537 {
538 Status = _SEH_GetExceptionCode();
539 }
540 _SEH_END;
541
542 if (!NT_SUCCESS(Status))
543 {
544 LpcpFreeToPortZone(Message, 0);
545 ObDereferenceObject(Port);
546 return Status;
547 }
548
549 /* Acquire the LPC lock */
550 KeAcquireGuardedMutex(&LpcpLock);
551
552 /* Right now clear the port context */
553 Message->PortContext = NULL;
554
555 /* Check if this is a not connection port */
556 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
557 {
558 /* We want the connected port */
559 QueuePort = Port->ConnectedPort;
560 if (!QueuePort)
561 {
562 /* We have no connected port, fail */
563 LpcpFreeToPortZone(Message, 3);
564 ObDereferenceObject(Port);
565 return STATUS_PORT_DISCONNECTED;
566 }
567
568 /* This will be the rundown port */
569 ReplyPort = QueuePort;
570
571 /* Check if this is a communication port */
572 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
573 {
574 /* Copy the port context and use the connection port */
575 Message->PortContext = QueuePort->PortContext;
576 ConnectionPort = QueuePort = Port->ConnectionPort;
577 if (!ConnectionPort)
578 {
579 /* Fail */
580 LpcpFreeToPortZone(Message, 3);
581 ObDereferenceObject(Port);
582 return STATUS_PORT_DISCONNECTED;
583 }
584 }
585 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) !=
586 LPCP_COMMUNICATION_PORT)
587 {
588 /* Use the connection port for anything but communication ports */
589 ConnectionPort = QueuePort = Port->ConnectionPort;
590 if (!ConnectionPort)
591 {
592 /* Fail */
593 LpcpFreeToPortZone(Message, 3);
594 ObDereferenceObject(Port);
595 return STATUS_PORT_DISCONNECTED;
596 }
597 }
598
599 /* Reference the connection port if it exists */
600 if (ConnectionPort) ObReferenceObject(ConnectionPort);
601 }
602 else
603 {
604 /* Otherwise, for a connection port, use the same port object */
605 QueuePort = ReplyPort = Port;
606 }
607
608 /* No reply thread */
609 Message->RepliedToThread = NULL;
610 Message->SenderPort = Port;
611
612 /* Generate the Message ID and set it */
613 Message->Request.MessageId = LpcpNextMessageId++;
614 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
615 Message->Request.CallbackId = 0;
616
617 /* Set the message ID for our thread now */
618 Thread->LpcReplyMessageId = Message->Request.MessageId;
619 Thread->LpcReplyMessage = NULL;
620
621 /* Insert the message in our chain */
622 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
623 InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain);
624 LpcpSetPortToThread(Thread, Port);
625
626 /* Release the lock and get the semaphore we'll use later */
627 KeEnterCriticalRegion();
628 KeReleaseGuardedMutex(&LpcpLock);
629 Semaphore = QueuePort->MsgQueue.Semaphore;
630
631 /* If this is a waitable port, wake it up */
632 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
633 {
634 /* Wake it */
635 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
636 }
637 }
638
639 /* Now release the semaphore */
640 LpcpCompleteWait(Semaphore);
641 KeLeaveCriticalRegion();
642
643 /* And let's wait for the reply */
644 LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode);
645
646 /* Acquire the LPC lock */
647 KeAcquireGuardedMutex(&LpcpLock);
648
649 /* Get the LPC Message and clear our thread's reply data */
650 Message = LpcpGetMessageFromThread(Thread);
651 Thread->LpcReplyMessage = NULL;
652 Thread->LpcReplyMessageId = 0;
653
654 /* Check if we have anything on the reply chain*/
655 if (!IsListEmpty(&Thread->LpcReplyChain))
656 {
657 /* Remove this thread and reinitialize the list */
658 RemoveEntryList(&Thread->LpcReplyChain);
659 InitializeListHead(&Thread->LpcReplyChain);
660 }
661
662 /* Release the lock */
663 KeReleaseGuardedMutex(&LpcpLock);
664
665 /* Check if we got a reply */
666 if (Status == STATUS_SUCCESS)
667 {
668 /* Check if we have a valid message */
669 if (Message)
670 {
671 LPCTRACE(LPC_SEND_DEBUG,
672 "Reply Messages: %p/%p\n",
673 &Message->Request,
674 (&Message->Request) + 1);
675
676 /* Move the message */
677 _SEH_TRY
678 {
679 LpcpMoveMessage(LpcReply,
680 &Message->Request,
681 (&Message->Request) + 1,
682 0,
683 NULL);
684 }
685 _SEH_HANDLE
686 {
687 Status = _SEH_GetExceptionCode();
688 }
689 _SEH_END;
690
691 /* Check if this is an LPC request with data information */
692 if ((LpcpGetMessageType(&Message->Request) == LPC_REQUEST) &&
693 (Message->Request.u2.s2.DataInfoOffset))
694 {
695 /* Save the data information */
696 LpcpSaveDataInfoMessage(Port, Message, 0);
697 }
698 else
699 {
700 /* Otherwise, just free it */
701 LpcpFreeToPortZone(Message, 0);
702 }
703 }
704 else
705 {
706 /* We don't have a reply */
707 Status = STATUS_LPC_REPLY_LOST;
708 }
709 }
710 else
711 {
712 /* The wait failed, free the message */
713 if (Message) LpcpFreeToPortZone(Message, 0);
714 }
715
716 /* All done */
717 LPCTRACE(LPC_SEND_DEBUG,
718 "Port: %p. Status: %p\n",
719 Port,
720 Status);
721 ObDereferenceObject(Port);
722 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
723 return Status;
724 }
725
726 /* EOF */