dad5a35c86517824bbbdd71d54e867dba0114339
[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 = FALSE;
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 MessageType = LpcpGetMessageType(LpcRequest);
202 switch (MessageType)
203 {
204 /* No type */
205 case 0:
206
207 /* Assume LPC request */
208 MessageType = LPC_REQUEST;
209 break;
210
211 /* LPC request callback */
212 case LPC_REQUEST:
213
214 /* This is a callback */
215 Callback = TRUE;
216 break;
217
218 /* Anything else */
219 case LPC_CLIENT_DIED:
220 case LPC_PORT_CLOSED:
221 case LPC_EXCEPTION:
222 case LPC_DEBUG_EVENT:
223 case LPC_ERROR_EVENT:
224
225 /* Nothing to do */
226 break;
227
228 default:
229
230 /* Invalid message type */
231 return STATUS_INVALID_PARAMETER;
232 }
233
234 /* Set the request type */
235 LpcRequest->u2.s2.Type = MessageType;
236
237 /* Validate the message length */
238 if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) ||
239 ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength))
240 {
241 /* Fail */
242 return STATUS_PORT_MESSAGE_TOO_LONG;
243 }
244
245 /* Allocate a message from the port zone */
246 Message = LpcpAllocateFromPortZone();
247 if (!Message)
248 {
249 /* Fail if we couldn't allocate a message */
250 return STATUS_NO_MEMORY;
251 }
252
253 /* Check if this is a callback */
254 if (Callback)
255 {
256 /* FIXME: TODO */
257 Semaphore = NULL; // we'd use the Thread Semaphore here
258 ASSERT(FALSE);
259 return STATUS_NOT_IMPLEMENTED;
260 }
261 else
262 {
263 /* No callback, just copy the message */
264 LpcpMoveMessage(&Message->Request,
265 LpcRequest,
266 LpcRequest + 1,
267 0,
268 &Thread->Cid);
269
270 /* Acquire the LPC lock */
271 KeAcquireGuardedMutex(&LpcpLock);
272
273 /* Right now clear the port context */
274 Message->PortContext = NULL;
275
276 /* Check if this is a not connection port */
277 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
278 {
279 /* We want the connected port */
280 QueuePort = Port->ConnectedPort;
281 if (!QueuePort)
282 {
283 /* We have no connected port, fail */
284 LpcpFreeToPortZone(Message, 3);
285 return STATUS_PORT_DISCONNECTED;
286 }
287
288 /* This will be the rundown port */
289 ReplyPort = QueuePort;
290
291 /* Check if this is a communication port */
292 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
293 {
294 /* Copy the port context and use the connection port */
295 Message->PortContext = QueuePort->PortContext;
296 ConnectionPort = QueuePort = Port->ConnectionPort;
297 if (!ConnectionPort)
298 {
299 /* Fail */
300 LpcpFreeToPortZone(Message, 3);
301 return STATUS_PORT_DISCONNECTED;
302 }
303 }
304 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) !=
305 LPCP_COMMUNICATION_PORT)
306 {
307 /* Use the connection port for anything but communication ports */
308 ConnectionPort = QueuePort = Port->ConnectionPort;
309 if (!ConnectionPort)
310 {
311 /* Fail */
312 LpcpFreeToPortZone(Message, 3);
313 return STATUS_PORT_DISCONNECTED;
314 }
315 }
316
317 /* Reference the connection port if it exists */
318 if (ConnectionPort) ObReferenceObject(ConnectionPort);
319 }
320 else
321 {
322 /* Otherwise, for a connection port, use the same port object */
323 QueuePort = ReplyPort = Port;
324 }
325
326 /* No reply thread */
327 Message->RepliedToThread = NULL;
328 Message->SenderPort = Port;
329
330 /* Generate the Message ID and set it */
331 Message->Request.MessageId = LpcpNextMessageId++;
332 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
333 Message->Request.CallbackId = 0;
334
335 /* Set the message ID for our thread now */
336 Thread->LpcReplyMessageId = Message->Request.MessageId;
337 Thread->LpcReplyMessage = NULL;
338
339 /* Insert the message in our chain */
340 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
341 InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain);
342 LpcpSetPortToThread(Thread, Port);
343
344 /* Release the lock and get the semaphore we'll use later */
345 KeEnterCriticalRegion();
346 KeReleaseGuardedMutex(&LpcpLock);
347 Semaphore = QueuePort->MsgQueue.Semaphore;
348
349 /* If this is a waitable port, wake it up */
350 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
351 {
352 /* Wake it */
353 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
354 }
355 }
356
357 /* Now release the semaphore */
358 LpcpCompleteWait(Semaphore);
359 KeLeaveCriticalRegion();
360
361 /* And let's wait for the reply */
362 LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode);
363
364 /* Acquire the LPC lock */
365 KeAcquireGuardedMutex(&LpcpLock);
366
367 /* Get the LPC Message and clear our thread's reply data */
368 Message = LpcpGetMessageFromThread(Thread);
369 Thread->LpcReplyMessage = NULL;
370 Thread->LpcReplyMessageId = 0;
371
372 /* Check if we have anything on the reply chain*/
373 if (!IsListEmpty(&Thread->LpcReplyChain))
374 {
375 /* Remove this thread and reinitialize the list */
376 RemoveEntryList(&Thread->LpcReplyChain);
377 InitializeListHead(&Thread->LpcReplyChain);
378 }
379
380 /* Release the lock */
381 KeReleaseGuardedMutex(&LpcpLock);
382
383 /* Check if we got a reply */
384 if (Status == STATUS_SUCCESS)
385 {
386 /* Check if we have a valid message */
387 if (Message)
388 {
389 LPCTRACE(LPC_SEND_DEBUG,
390 "Reply Messages: %p/%p\n",
391 &Message->Request,
392 (&Message->Request) + 1);
393
394 /* Move the message */
395 LpcpMoveMessage(LpcReply,
396 &Message->Request,
397 (&Message->Request) + 1,
398 0,
399 NULL);
400
401 /* Acquire the lock */
402 KeAcquireGuardedMutex(&LpcpLock);
403
404 /* Check if we replied to a thread */
405 if (Message->RepliedToThread)
406 {
407 /* Dereference */
408 ObDereferenceObject(Message->RepliedToThread);
409 Message->RepliedToThread = NULL;
410 }
411
412
413 /* Free the message */
414 LpcpFreeToPortZone(Message, 3);
415 }
416 else
417 {
418 /* We don't have a reply */
419 Status = STATUS_LPC_REPLY_LOST;
420 }
421 }
422 else
423 {
424 /* The wait failed, free the message */
425 if (Message) LpcpFreeToPortZone(Message, 0);
426 }
427
428 /* All done */
429 LPCTRACE(LPC_SEND_DEBUG,
430 "Port: %p. Status: %p\n",
431 Port,
432 Status);
433
434 /* Dereference the connection port */
435 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
436 return Status;
437 }
438
439 /*
440 * @unimplemented
441 */
442 NTSTATUS
443 NTAPI
444 NtRequestPort(IN HANDLE PortHandle,
445 IN PPORT_MESSAGE LpcMessage)
446 {
447 UNIMPLEMENTED;
448 return STATUS_NOT_IMPLEMENTED;
449 }
450
451 /*
452 * @implemented
453 */
454 NTSTATUS
455 NTAPI
456 NtRequestWaitReplyPort(IN HANDLE PortHandle,
457 IN PPORT_MESSAGE LpcRequest,
458 IN OUT PPORT_MESSAGE LpcReply)
459 {
460 PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL;
461 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
462 NTSTATUS Status;
463 PLPCP_MESSAGE Message;
464 PETHREAD Thread = PsGetCurrentThread();
465 BOOLEAN Callback;
466 PKSEMAPHORE Semaphore;
467 ULONG MessageType;
468 PAGED_CODE();
469 LPCTRACE(LPC_SEND_DEBUG,
470 "Handle: %lx. Messages: %p/%p. Type: %lx\n",
471 PortHandle,
472 LpcRequest,
473 LpcReply,
474 LpcpGetMessageType(LpcRequest));
475
476 /* Check if the thread is dying */
477 if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING;
478
479 /* Check if this is an LPC Request */
480 if (LpcpGetMessageType(LpcRequest) == LPC_REQUEST)
481 {
482 /* Then it's a callback */
483 Callback = TRUE;
484 }
485 else if (LpcpGetMessageType(LpcRequest))
486 {
487 /* This is a not kernel-mode message */
488 return STATUS_INVALID_PARAMETER;
489 }
490 else
491 {
492 /* This is a kernel-mode message without a callback */
493 LpcRequest->u2.s2.Type |= LPC_REQUEST;
494 Callback = FALSE;
495 }
496
497 /* Get the message type */
498 MessageType = LpcRequest->u2.s2.Type;
499
500 /* Validate the length */
501 if (((ULONG)LpcRequest->u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
502 (ULONG)LpcRequest->u1.s1.TotalLength)
503 {
504 /* Fail */
505 return STATUS_INVALID_PARAMETER;
506 }
507
508 /* Reference the object */
509 Status = ObReferenceObjectByHandle(PortHandle,
510 0,
511 LpcPortObjectType,
512 PreviousMode,
513 (PVOID*)&Port,
514 NULL);
515 if (!NT_SUCCESS(Status)) return Status;
516
517 /* Validate the message length */
518 if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) ||
519 ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength))
520 {
521 /* Fail */
522 ObDereferenceObject(Port);
523 return STATUS_PORT_MESSAGE_TOO_LONG;
524 }
525
526 /* Allocate a message from the port zone */
527 Message = LpcpAllocateFromPortZone();
528 if (!Message)
529 {
530 /* Fail if we couldn't allocate a message */
531 ObDereferenceObject(Port);
532 return STATUS_NO_MEMORY;
533 }
534
535 /* Check if this is a callback */
536 if (Callback)
537 {
538 /* FIXME: TODO */
539 Semaphore = NULL; // we'd use the Thread Semaphore here
540 ASSERT(FALSE);
541 }
542 else
543 {
544 /* No callback, just copy the message */
545 _SEH2_TRY
546 {
547 /* Copy it */
548 LpcpMoveMessage(&Message->Request,
549 LpcRequest,
550 LpcRequest + 1,
551 MessageType,
552 &Thread->Cid);
553 }
554 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
555 {
556 /* Fail */
557 LpcpFreeToPortZone(Message, 0);
558 ObDereferenceObject(Port);
559 _SEH2_YIELD(return _SEH2_GetExceptionCode());
560 }
561 _SEH2_END;
562
563 /* Acquire the LPC lock */
564 KeAcquireGuardedMutex(&LpcpLock);
565
566 /* Right now clear the port context */
567 Message->PortContext = NULL;
568
569 /* Check if this is a not connection port */
570 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
571 {
572 /* We want the connected port */
573 QueuePort = Port->ConnectedPort;
574 if (!QueuePort)
575 {
576 /* We have no connected port, fail */
577 LpcpFreeToPortZone(Message, 3);
578 ObDereferenceObject(Port);
579 return STATUS_PORT_DISCONNECTED;
580 }
581
582 /* This will be the rundown port */
583 ReplyPort = QueuePort;
584
585 /* Check if this is a communication port */
586 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
587 {
588 /* Copy the port context and use the connection port */
589 Message->PortContext = QueuePort->PortContext;
590 ConnectionPort = QueuePort = Port->ConnectionPort;
591 if (!ConnectionPort)
592 {
593 /* Fail */
594 LpcpFreeToPortZone(Message, 3);
595 ObDereferenceObject(Port);
596 return STATUS_PORT_DISCONNECTED;
597 }
598 }
599 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) !=
600 LPCP_COMMUNICATION_PORT)
601 {
602 /* Use the connection port for anything but communication ports */
603 ConnectionPort = QueuePort = Port->ConnectionPort;
604 if (!ConnectionPort)
605 {
606 /* Fail */
607 LpcpFreeToPortZone(Message, 3);
608 ObDereferenceObject(Port);
609 return STATUS_PORT_DISCONNECTED;
610 }
611 }
612
613 /* Reference the connection port if it exists */
614 if (ConnectionPort) ObReferenceObject(ConnectionPort);
615 }
616 else
617 {
618 /* Otherwise, for a connection port, use the same port object */
619 QueuePort = ReplyPort = Port;
620 }
621
622 /* No reply thread */
623 Message->RepliedToThread = NULL;
624 Message->SenderPort = Port;
625
626 /* Generate the Message ID and set it */
627 Message->Request.MessageId = LpcpNextMessageId++;
628 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
629 Message->Request.CallbackId = 0;
630
631 /* Set the message ID for our thread now */
632 Thread->LpcReplyMessageId = Message->Request.MessageId;
633 Thread->LpcReplyMessage = NULL;
634
635 /* Insert the message in our chain */
636 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
637 InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain);
638 LpcpSetPortToThread(Thread, Port);
639
640 /* Release the lock and get the semaphore we'll use later */
641 KeEnterCriticalRegion();
642 KeReleaseGuardedMutex(&LpcpLock);
643 Semaphore = QueuePort->MsgQueue.Semaphore;
644
645 /* If this is a waitable port, wake it up */
646 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
647 {
648 /* Wake it */
649 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
650 }
651 }
652
653 /* Now release the semaphore */
654 LpcpCompleteWait(Semaphore);
655 KeLeaveCriticalRegion();
656
657 /* And let's wait for the reply */
658 LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode);
659
660 /* Acquire the LPC lock */
661 KeAcquireGuardedMutex(&LpcpLock);
662
663 /* Get the LPC Message and clear our thread's reply data */
664 Message = LpcpGetMessageFromThread(Thread);
665 Thread->LpcReplyMessage = NULL;
666 Thread->LpcReplyMessageId = 0;
667
668 /* Check if we have anything on the reply chain*/
669 if (!IsListEmpty(&Thread->LpcReplyChain))
670 {
671 /* Remove this thread and reinitialize the list */
672 RemoveEntryList(&Thread->LpcReplyChain);
673 InitializeListHead(&Thread->LpcReplyChain);
674 }
675
676 /* Release the lock */
677 KeReleaseGuardedMutex(&LpcpLock);
678
679 /* Check if we got a reply */
680 if (Status == STATUS_SUCCESS)
681 {
682 /* Check if we have a valid message */
683 if (Message)
684 {
685 LPCTRACE(LPC_SEND_DEBUG,
686 "Reply Messages: %p/%p\n",
687 &Message->Request,
688 (&Message->Request) + 1);
689
690 /* Move the message */
691 _SEH2_TRY
692 {
693 LpcpMoveMessage(LpcReply,
694 &Message->Request,
695 (&Message->Request) + 1,
696 0,
697 NULL);
698 }
699 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
700 {
701 Status = _SEH2_GetExceptionCode();
702 }
703 _SEH2_END;
704
705 /* Check if this is an LPC request with data information */
706 if ((LpcpGetMessageType(&Message->Request) == LPC_REQUEST) &&
707 (Message->Request.u2.s2.DataInfoOffset))
708 {
709 /* Save the data information */
710 LpcpSaveDataInfoMessage(Port, Message, 0);
711 }
712 else
713 {
714 /* Otherwise, just free it */
715 LpcpFreeToPortZone(Message, 0);
716 }
717 }
718 else
719 {
720 /* We don't have a reply */
721 Status = STATUS_LPC_REPLY_LOST;
722 }
723 }
724 else
725 {
726 /* The wait failed, free the message */
727 if (Message) LpcpFreeToPortZone(Message, 0);
728 }
729
730 /* All done */
731 LPCTRACE(LPC_SEND_DEBUG,
732 "Port: %p. Status: %p\n",
733 Port,
734 Status);
735 ObDereferenceObject(Port);
736 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
737 return Status;
738 }
739
740 /* EOF */