update for HEAD-2002110401
[reactos.git] / ntoskrnl / lpc / reply.c
1 /* $Id$
2  * 
3  * COPYRIGHT:       See COPYING in the top level directory
4  * PROJECT:         ReactOS kernel
5  * FILE:            ntoskrnl/lpc/reply.c
6  * PURPOSE:         Communication mechanism
7  * PROGRAMMER:      David Welch (welch@cwcom.net)
8  * UPDATE HISTORY:
9  *                  Created 22/05/98
10  */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <internal/ob.h>
16 #include <internal/port.h>
17 #include <internal/dbg.h>
18 #include <internal/pool.h>
19 #include <internal/safe.h>
20
21 #define NDEBUG
22 #include <internal/debug.h>
23
24 /* GLOBALS *******************************************************************/
25
26 #define TAG_LPC_MESSAGE   TAG('L', 'P', 'C', 'M')
27
28 /* FUNCTIONS *****************************************************************/
29
30 /**********************************************************************
31  * NAME
32  *
33  * DESCRIPTION
34  *
35  * ARGUMENTS
36  *
37  * RETURN VALUE
38  *
39  * REVISIONS
40  *
41  */
42 NTSTATUS STDCALL
43 EiReplyOrRequestPort (IN        PEPORT          Port, 
44                       IN        PLPC_MESSAGE    LpcReply, 
45                       IN        ULONG           MessageType,
46                       IN        PEPORT          Sender)
47 {
48    KIRQL oldIrql;
49    PQUEUEDMESSAGE MessageReply;
50    
51    if (Port == NULL)
52      {
53        KeBugCheck(0);
54      }
55
56    MessageReply = ExAllocatePoolWithTag(NonPagedPool, sizeof(QUEUEDMESSAGE),
57                                         TAG_LPC_MESSAGE);
58    MessageReply->Sender = Sender;
59    
60    if (LpcReply != NULL)
61      {
62         memcpy(&MessageReply->Message, LpcReply, LpcReply->MessageSize);
63      }
64    
65    MessageReply->Message.Cid.UniqueProcess = PsGetCurrentProcessId();
66    MessageReply->Message.Cid.UniqueThread = PsGetCurrentThreadId();
67    MessageReply->Message.MessageType = MessageType;
68    MessageReply->Message.MessageId = InterlockedIncrement(&EiNextLpcMessageId);
69    
70    KeAcquireSpinLock(&Port->Lock, &oldIrql);
71    EiEnqueueMessagePort(Port, MessageReply);
72    KeReleaseSpinLock(&Port->Lock, oldIrql);
73    
74    return(STATUS_SUCCESS);
75 }
76
77
78 /**********************************************************************
79  * NAME                                                 EXPORTED
80  *
81  * DESCRIPTION
82  *
83  * ARGUMENTS
84  *
85  * RETURN VALUE
86  *
87  * REVISIONS
88  *
89  */
90 NTSTATUS STDCALL
91 NtReplyPort (IN HANDLE          PortHandle,
92              IN PLPC_MESSAGE    LpcReply)
93 {
94    NTSTATUS Status;
95    PEPORT Port;
96    
97    DPRINT("NtReplyPort(PortHandle %x, LpcReply %x)\n", PortHandle, LpcReply);
98    
99    Status = ObReferenceObjectByHandle(PortHandle,
100                                       PORT_ALL_ACCESS,   /* AccessRequired */
101                                       ExPortType,
102                                       UserMode,
103                                       (PVOID*)&Port,
104                                       NULL);
105    if (!NT_SUCCESS(Status))
106      {
107         DPRINT("NtReplyPort() = %x\n", Status);
108         return(Status);
109      }
110    
111    Status = EiReplyOrRequestPort(Port->OtherPort, 
112                                  LpcReply, 
113                                  LPC_REPLY,
114                                  Port);
115    KeReleaseSemaphore(&Port->OtherPort->Semaphore, IO_NO_INCREMENT, 1, FALSE);
116    
117    ObDereferenceObject(Port);
118    
119    return(Status);
120 }
121
122
123 /**********************************************************************
124  * NAME                                                 EXPORTED
125  *      NtReplyWaitReceivePortEx
126  *      
127  * DESCRIPTION
128  *      Can be used with waitable ports.
129  *      Present only in w2k+.
130  *      
131  * ARGUMENTS
132  *      PortHandle
133  *      PortId
134  *      LpcReply
135  *      LpcMessage
136  *      Timeout
137  *
138  * RETURN VALUE
139  *
140  * REVISIONS
141  *
142  */
143 NTSTATUS STDCALL
144 NtReplyWaitReceivePortEx(IN  HANDLE             PortHandle,
145                          OUT PULONG             PortId,
146                          IN  PLPC_MESSAGE       LpcReply,     
147                          OUT PLPC_MESSAGE       LpcMessage,
148                          IN  PLARGE_INTEGER     Timeout)
149 {
150    NTSTATUS Status;
151    PEPORT Port;
152    KIRQL oldIrql;
153    PQUEUEDMESSAGE Request;
154    BOOLEAN Disconnected;
155    LARGE_INTEGER to;
156    
157    DPRINT("NtReplyWaitReceivePortEx(PortHandle %x, LpcReply %x, "
158           "LpcMessage %x)\n", PortHandle, LpcReply, LpcMessage);
159    
160    Status = ObReferenceObjectByHandle(PortHandle,
161                                       PORT_ALL_ACCESS,
162                                       ExPortType,
163                                       UserMode,
164                                       (PVOID*)&Port,
165                                       NULL);
166    if (!NT_SUCCESS(Status))
167      {
168         DPRINT1("NtReplyWaitReceivePortEx() = %x\n", Status);
169         return(Status);
170      }
171    if( Port->State == EPORT_DISCONNECTED )
172      {
173        /* If the port is disconnected, force the timeout to be 0
174         * so we don't wait for new messages, because there won't be
175         * any, only try to remove any existing messages
176         */
177        Disconnected = TRUE;
178        to.QuadPart = 0;
179        Timeout = &to;
180      }
181    else Disconnected = FALSE;
182    
183    /*
184     * Send the reply, only if port is connected
185     */
186    if (LpcReply != NULL && !Disconnected)
187      {
188         Status = EiReplyOrRequestPort(Port->OtherPort, 
189                                       LpcReply,
190                                       LPC_REPLY,
191                                       Port);
192         KeReleaseSemaphore(&Port->OtherPort->Semaphore, IO_NO_INCREMENT, 1, 
193                            FALSE);
194         
195         if (!NT_SUCCESS(Status))
196           {
197              ObDereferenceObject(Port);
198              DPRINT1("NtReplyWaitReceivePortEx() = %x\n", Status);
199              return(Status);
200           }
201      }
202    
203    /*
204     * Want for a message to be received
205     */
206    Status = KeWaitForSingleObject(&Port->Semaphore,
207                                   UserRequest,
208                                   UserMode,
209                                   FALSE,
210                                   Timeout);
211    if( Status == STATUS_TIMEOUT )
212      {
213        /*
214         * if the port is disconnected, and there are no remaining messages,
215         * return STATUS_PORT_DISCONNECTED
216         */
217        ObDereferenceObject(Port);
218        return(Disconnected ? STATUS_PORT_DISCONNECTED : STATUS_TIMEOUT);
219      }
220    
221    if (!NT_SUCCESS(Status))
222      {
223        DPRINT1("NtReplyWaitReceivePortEx() = %x\n", Status);
224        ObDereferenceObject(Port);
225        return(Status);
226      }
227
228    /*
229     * Dequeue the message
230     */
231    KeAcquireSpinLock(&Port->Lock, &oldIrql);
232    Request = EiDequeueMessagePort(Port);
233    
234    if (Request->Message.MessageType == LPC_CONNECTION_REQUEST)
235      {
236        LPC_MESSAGE Header;
237        PEPORT_CONNECT_REQUEST_MESSAGE CRequest;
238
239        CRequest = (PEPORT_CONNECT_REQUEST_MESSAGE)&Request->Message;
240        memcpy(&Header, &Request->Message, sizeof(LPC_MESSAGE));
241        Header.DataSize = CRequest->ConnectDataLength;
242        Header.MessageSize = Header.DataSize + sizeof(LPC_MESSAGE);
243        Status = MmCopyToCaller(LpcMessage, &Header, sizeof(LPC_MESSAGE));
244        if (!NT_SUCCESS(Status))
245          {
246            Status = MmCopyToCaller((PVOID)(LpcMessage + 1),
247                                    CRequest->ConnectData,
248                                    CRequest->ConnectDataLength);
249          }
250      }
251    else
252      {
253        Status = MmCopyToCaller(LpcMessage, &Request->Message,
254                                Request->Message.MessageSize);
255      }
256    if (!NT_SUCCESS(Status))
257      {
258        /*
259         * Copying the message to the caller's buffer failed so 
260         * undo what we did and return.
261         * FIXME: Also increment semaphore.
262         */
263        EiEnqueueMessageAtHeadPort(Port, Request);
264        KeReleaseSpinLock(&Port->Lock, oldIrql);
265        ObDereferenceObject(Port);
266        return(Status);
267      }
268    if (Request->Message.MessageType == LPC_CONNECTION_REQUEST)
269      {
270        EiEnqueueConnectMessagePort(Port, Request);
271        KeReleaseSpinLock(&Port->Lock, oldIrql);
272      }
273    else
274      {
275        KeReleaseSpinLock(&Port->Lock, oldIrql);
276        ExFreePool(Request);
277      }
278    
279    /*
280     * Dereference the port
281     */
282    ObDereferenceObject(Port);
283    return(STATUS_SUCCESS);
284 }
285
286
287 /**********************************************************************
288  * NAME                                         EXPORTED
289  *      NtReplyWaitReceivePort
290  *      
291  * DESCRIPTION
292  *      Can be used with waitable ports.
293  *      
294  * ARGUMENTS
295  *      PortHandle
296  *      PortId
297  *      LpcReply
298  *      LpcMessage
299  *
300  * RETURN VALUE
301  *
302  * REVISIONS
303  *
304  */
305 NTSTATUS STDCALL
306 NtReplyWaitReceivePort (IN  HANDLE              PortHandle,
307                         OUT PULONG              PortId,
308                         IN  PLPC_MESSAGE        LpcReply,     
309                         OUT PLPC_MESSAGE        LpcMessage)
310 {
311   return(NtReplyWaitReceivePortEx (PortHandle,
312                                    PortId,
313                                    LpcReply,
314                                    LpcMessage,
315                                    NULL));
316 }
317
318 /**********************************************************************
319  * NAME
320  *
321  * DESCRIPTION
322  *
323  * ARGUMENTS
324  *
325  * RETURN VALUE
326  *
327  * REVISIONS
328  *
329  */
330 NTSTATUS STDCALL
331 NtReplyWaitReplyPort (HANDLE            PortHandle,
332                       PLPC_MESSAGE      ReplyMessage)
333 {
334    UNIMPLEMENTED;
335 }
336
337
338 /* EOF */