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)
9 /* INCLUDES ******************************************************************/
14 #include <internal/debug.h>
16 /* PUBLIC FUNCTIONS **********************************************************/
23 LpcRequestPort(IN PVOID PortObject
,
24 IN PPORT_MESSAGE LpcMessage
)
26 PLPCP_PORT_OBJECT Port
= (PLPCP_PORT_OBJECT
)PortObject
, QueuePort
;
28 PLPCP_MESSAGE Message
;
29 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
31 LPCTRACE(LPC_SEND_DEBUG
, "Port: %p. Message: %p\n", Port
, LpcMessage
);
33 /* Check if this is a non-datagram message */
34 if (LpcMessage
->u2
.s2
.Type
)
36 /* Get the message type */
37 MessageType
= LpcpGetMessageType(LpcMessage
);
40 if ((MessageType
< LPC_DATAGRAM
) || (MessageType
> LPC_CLIENT_DIED
))
43 return STATUS_INVALID_PARAMETER
;
46 /* Mark this as a kernel-mode message only if we really came from there */
47 if ((PreviousMode
== KernelMode
) &&
48 (LpcMessage
->u2
.s2
.Type
& LPC_KERNELMODE_MESSAGE
))
50 /* We did, this is a kernel mode message */
51 MessageType
|= LPC_KERNELMODE_MESSAGE
;
56 /* This is a datagram */
57 MessageType
= LPC_DATAGRAM
;
60 /* Can't have data information on this type of call */
61 if (LpcMessage
->u2
.s2
.DataInfoOffset
) return STATUS_INVALID_PARAMETER
;
63 /* Validate message sizes */
64 if ((LpcMessage
->u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
65 (LpcMessage
->u1
.s1
.TotalLength
<= LpcMessage
->u1
.s1
.DataLength
))
68 return STATUS_PORT_MESSAGE_TOO_LONG
;
71 /* Allocate a new message */
72 Message
= LpcpAllocateFromPortZone();
73 if (!Message
) return STATUS_NO_MEMORY
;
75 /* Clear the context */
76 Message
->PortContext
= NULL
;
78 /* Copy the message */
79 LpcpMoveMessage(&Message
->Request
,
83 &PsGetCurrentThread()->Cid
);
85 /* Acquire the LPC lock */
86 KeAcquireGuardedMutex(&LpcpLock
);
88 /* Check if this is anything but a connection port */
89 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CONNECTION_PORT
)
91 /* The queue port is the connected port */
92 QueuePort
= Port
->ConnectedPort
;
95 /* Check if this is a client port */
96 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) == LPCP_CLIENT_PORT
)
98 /* Then copy the context */
99 Message
->PortContext
= QueuePort
->PortContext
;
100 QueuePort
= Port
->ConnectionPort
;
102 else if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_COMMUNICATION_PORT
)
104 /* Any other kind of port, use the connection port */
105 QueuePort
= Port
->ConnectionPort
;
111 /* For connection ports, use the port itself */
112 QueuePort
= PortObject
;
115 /* Make sure we have a port */
118 /* Generate the Message ID and set it */
119 Message
->Request
.MessageId
= LpcpNextMessageId
++;
120 if (!LpcpNextMessageId
) LpcpNextMessageId
= 1;
121 Message
->Request
.CallbackId
= 0;
123 /* No Message ID for the thread */
124 PsGetCurrentThread()->LpcReplyMessageId
= 0;
126 /* Insert the message in our chain */
127 InsertTailList(&QueuePort
->MsgQueue
.ReceiveHead
, &Message
->Entry
);
129 /* Release the lock and release the semaphore */
130 KeReleaseGuardedMutex(&LpcpLock
);
131 LpcpCompleteWait(QueuePort
->MsgQueue
.Semaphore
);
133 /* If this is a waitable port, wake it up */
134 if (QueuePort
->Flags
& LPCP_WAITABLE_PORT
)
137 KeSetEvent(&QueuePort
->WaitEvent
, IO_NO_INCREMENT
, FALSE
);
141 LPCTRACE(LPC_SEND_DEBUG
, "Port: %p. Message: %p\n", QueuePort
, Message
);
142 return STATUS_SUCCESS
;
145 /* If we got here, then free the message and fail */
146 LpcpFreeToPortZone(Message
, TRUE
);
147 KeReleaseGuardedMutex(&LpcpLock
);
148 return STATUS_PORT_DISCONNECTED
;
156 LpcRequestWaitReplyPort(IN PVOID Port
,
157 IN PPORT_MESSAGE LpcMessageRequest
,
158 OUT PPORT_MESSAGE LpcMessageReply
)
161 return STATUS_NOT_IMPLEMENTED
;
169 NtRequestPort(IN HANDLE PortHandle
,
170 IN PPORT_MESSAGE LpcMessage
)
173 return STATUS_NOT_IMPLEMENTED
;
181 NtRequestWaitReplyPort(IN HANDLE PortHandle
,
182 IN PPORT_MESSAGE LpcRequest
,
183 IN OUT PPORT_MESSAGE LpcReply
)
185 PLPCP_PORT_OBJECT Port
, QueuePort
, ReplyPort
;
186 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
188 PLPCP_MESSAGE Message
;
189 PETHREAD Thread
= PsGetCurrentThread();
191 PKSEMAPHORE Semaphore
;
194 LPCTRACE(LPC_SEND_DEBUG
,
195 "Handle: %lx. Messages: %p/%p. Type: %lx\n",
199 LpcpGetMessageType(LpcRequest
));
201 /* Check if the thread is dying */
202 if (Thread
->LpcExitThreadCalled
) return STATUS_THREAD_IS_TERMINATING
;
204 /* Check if this is an LPC Request */
205 if (LpcpGetMessageType(LpcRequest
) == LPC_REQUEST
)
207 /* Then it's a callback */
210 else if (LpcpGetMessageType(LpcRequest
))
212 /* This is a not kernel-mode message */
213 return STATUS_INVALID_PARAMETER
;
217 /* This is a kernel-mode message without a callback */
218 LpcRequest
->u2
.s2
.Type
|= LPC_REQUEST
;
222 /* Get the message type */
223 MessageType
= LpcRequest
->u2
.s2
.Type
;
225 /* Validate the length */
226 if ((LpcRequest
->u1
.s1
.DataLength
+ sizeof(PORT_MESSAGE
)) >
227 LpcRequest
->u1
.s1
.TotalLength
)
230 return STATUS_INVALID_PARAMETER
;
233 /* Reference the object */
234 Status
= ObReferenceObjectByHandle(PortHandle
,
240 if (!NT_SUCCESS(Status
)) return Status
;
242 /* Validate the message length */
243 if ((LpcRequest
->u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
244 (LpcRequest
->u1
.s1
.TotalLength
<= LpcRequest
->u1
.s1
.DataLength
))
247 ObDereferenceObject(Port
);
248 return STATUS_PORT_MESSAGE_TOO_LONG
;
251 /* Allocate a message from the port zone */
252 Message
= LpcpAllocateFromPortZone();
255 /* Fail if we couldn't allocate a message */
256 ObDereferenceObject(Port
);
257 return STATUS_NO_MEMORY
;
260 /* Check if this is a callback */
264 Semaphore
= NULL
; // we'd use the Thread Semaphore here
269 /* No callback, just copy the message */
270 LpcpMoveMessage(&Message
->Request
,
276 /* Acquire the LPC lock */
277 KeAcquireGuardedMutex(&LpcpLock
);
279 /* Right now clear the port context */
280 Message
->PortContext
= NULL
;
282 /* Check if this is a not connection port */
283 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CONNECTION_PORT
)
285 /* We want the connected port */
286 QueuePort
= Port
->ConnectedPort
;
289 /* We have no connected port, fail */
290 LpcpFreeToPortZone(Message
, TRUE
);
291 KeReleaseGuardedMutex(&LpcpLock
);
292 ObDereferenceObject(Port
);
293 return STATUS_PORT_DISCONNECTED
;
296 /* This will be the rundown port */
297 ReplyPort
= QueuePort
;
299 /* Check if this is a communication port */
300 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) == LPCP_CLIENT_PORT
)
302 /* Copy the port context and use the connection port */
303 Message
->PortContext
= ReplyPort
->PortContext
;
304 QueuePort
= Port
->ConnectionPort
;
306 else if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) !=
307 LPCP_COMMUNICATION_PORT
)
309 /* Use the connection port for anything but communication ports */
310 QueuePort
= Port
->ConnectionPort
;
315 /* Otherwise, for a connection port, use the same port object */
316 QueuePort
= ReplyPort
= Port
;
319 /* No reply thread */
320 Message
->RepliedToThread
= NULL
;
322 /* Generate the Message ID and set it */
323 Message
->Request
.MessageId
= LpcpNextMessageId
++;
324 if (!LpcpNextMessageId
) LpcpNextMessageId
= 1;
325 Message
->Request
.CallbackId
= 0;
327 /* Set the message ID for our thread now */
328 Thread
->LpcReplyMessageId
= Message
->Request
.MessageId
;
329 Thread
->LpcReplyMessage
= NULL
;
331 /* Insert the message in our chain */
332 InsertTailList(&QueuePort
->MsgQueue
.ReceiveHead
, &Message
->Entry
);
333 InsertTailList(&ReplyPort
->LpcReplyChainHead
, &Thread
->LpcReplyChain
);
335 /* Release the lock and get the semaphore we'll use later */
336 KeReleaseGuardedMutex(&LpcpLock
);
337 Semaphore
= QueuePort
->MsgQueue
.Semaphore
;
339 /* If this is a waitable port, wake it up */
340 if (QueuePort
->Flags
& LPCP_WAITABLE_PORT
)
343 KeSetEvent(&QueuePort
->WaitEvent
, IO_NO_INCREMENT
, FALSE
);
347 /* Now release the semaphore */
348 LpcpCompleteWait(Semaphore
);
350 /* And let's wait for the reply */
351 LpcpReplyWait(&Thread
->LpcReplySemaphore
, PreviousMode
);
353 /* Acquire the LPC lock */
354 KeAcquireGuardedMutex(&LpcpLock
);
356 /* Get the LPC Message and clear our thread's reply data */
357 Message
= Thread
->LpcReplyMessage
;
358 Thread
->LpcReplyMessage
= NULL
;
359 Thread
->LpcReplyMessageId
= 0;
361 /* Check if we have anything on the reply chain*/
362 if (!IsListEmpty(&Thread
->LpcReplyChain
))
364 /* Remove this thread and reinitialize the list */
365 RemoveEntryList(&Thread
->LpcReplyChain
);
366 InitializeListHead(&Thread
->LpcReplyChain
);
369 /* Release the lock */
370 KeReleaseGuardedMutex(&LpcpLock
);
372 /* Check if we got a reply */
373 if (Status
== STATUS_SUCCESS
)
375 /* Check if we have a valid message */
378 LPCTRACE(LPC_SEND_DEBUG
,
379 "Reply Messages: %p/%p\n",
381 (&Message
->Request
) + 1);
383 /* Move the message */
384 LpcpMoveMessage(LpcReply
,
386 (&Message
->Request
) + 1,
390 /* Check if this is an LPC request with data information */
391 if ((LpcpGetMessageType(&Message
->Request
) == LPC_REQUEST
) &&
392 (Message
->Request
.u2
.s2
.DataInfoOffset
))
394 /* Save the data information */
395 LpcpSaveDataInfoMessage(Port
, Message
);
399 /* Otherwise, just free it */
400 LpcpFreeToPortZone(Message
, FALSE
);
405 /* We don't have a reply */
406 Status
= STATUS_LPC_REPLY_LOST
;
411 /* The wait failed, free the message while holding the lock */
412 KeAcquireGuardedMutex(&LpcpLock
);
413 LpcpFreeToPortZone(Message
, TRUE
);
414 KeReleaseGuardedMutex(&LpcpLock
);
418 LPCTRACE(LPC_SEND_DEBUG
,
419 "Port: %p. Status: %p\n",
422 ObDereferenceObject(Port
);