Subversion Repositories HelenOS

Rev

Rev 2479 | Rev 2490 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 2479 Rev 2482
1
/*
1
/*
2
 * Copyright (c) 2006 Ondrej Palkovsky
2
 * Copyright (c) 2006 Ondrej Palkovsky
3
 * All rights reserved.
3
 * All rights reserved.
4
 *
4
 *
5
 * Redistribution and use in source and binary forms, with or without
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
6
 * modification, are permitted provided that the following conditions
7
 * are met:
7
 * are met:
8
 *
8
 *
9
 * - Redistributions of source code must retain the above copyright
9
 * - Redistributions of source code must retain the above copyright
10
 *   notice, this list of conditions and the following disclaimer.
10
 *   notice, this list of conditions and the following disclaimer.
11
 * - Redistributions in binary form must reproduce the above copyright
11
 * - Redistributions in binary form must reproduce the above copyright
12
 *   notice, this list of conditions and the following disclaimer in the
12
 *   notice, this list of conditions and the following disclaimer in the
13
 *   documentation and/or other materials provided with the distribution.
13
 *   documentation and/or other materials provided with the distribution.
14
 * - The name of the author may not be used to endorse or promote products
14
 * - The name of the author may not be used to endorse or promote products
15
 *   derived from this software without specific prior written permission.
15
 *   derived from this software without specific prior written permission.
16
 *
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 */
27
 */
28
 
28
 
29
/** @addtogroup libc
29
/** @addtogroup libc
30
 * @{
30
 * @{
31
 * @}
31
 * @}
32
 */
32
 */
33
 
33
 
34
/** @addtogroup libcipc IPC
34
/** @addtogroup libcipc IPC
35
 * @brief HelenOS uspace IPC
35
 * @brief HelenOS uspace IPC
36
 * @{
36
 * @{
37
 * @ingroup libc
37
 * @ingroup libc
38
 */
38
 */
39
/** @file
39
/** @file
40
 */
40
 */
41
 
41
 
42
#include <ipc/ipc.h>
42
#include <ipc/ipc.h>
43
#include <libc.h>
43
#include <libc.h>
44
#include <malloc.h>
44
#include <malloc.h>
45
#include <errno.h>
45
#include <errno.h>
46
#include <libadt/list.h>
46
#include <libadt/list.h>
47
#include <stdio.h>
47
#include <stdio.h>
48
#include <unistd.h>
48
#include <unistd.h>
49
#include <futex.h>
49
#include <futex.h>
50
#include <kernel/synch/synch.h>
50
#include <kernel/synch/synch.h>
51
#include <async.h>
51
#include <async.h>
52
#include <psthread.h>
52
#include <fibril.h>
53
 
53
 
54
/** Structure used for keeping track of sent asynchronous calls and queing
54
/** Structure used for keeping track of sent asynchronous calls and queing
55
 * unsent calls.
55
 * unsent calls.
56
 */
56
 */
57
typedef struct {
57
typedef struct {
58
    link_t list;
58
    link_t list;
59
 
59
 
60
    ipc_async_callback_t callback;
60
    ipc_async_callback_t callback;
61
    void *private;
61
    void *private;
62
    union {
62
    union {
63
        ipc_callid_t callid;
63
        ipc_callid_t callid;
64
        struct {
64
        struct {
65
            ipc_call_t data;
65
            ipc_call_t data;
66
            int phoneid;
66
            int phoneid;
67
        } msg;
67
        } msg;
68
    } u;
68
    } u;
69
    pstid_t ptid;   /**< Pseudothread waiting for sending this call. */
69
    fid_t fid;  /**< Fibril waiting for sending this call. */
70
} async_call_t;
70
} async_call_t;
71
 
71
 
72
LIST_INITIALIZE(dispatched_calls);
72
LIST_INITIALIZE(dispatched_calls);
73
 
73
 
74
/** List of asynchronous calls that were not accepted by kernel.
74
/** List of asynchronous calls that were not accepted by kernel.
75
 *
75
 *
76
 * It is protected by async_futex, because if the call cannot be sent into the
76
 * It is protected by async_futex, because if the call cannot be sent into the
77
 * kernel, the async framework is used automatically.
77
 * kernel, the async framework is used automatically.
78
 */
78
 */
79
LIST_INITIALIZE(queued_calls);
79
LIST_INITIALIZE(queued_calls);
80
 
80
 
81
static atomic_t ipc_futex = FUTEX_INITIALIZER;
81
static atomic_t ipc_futex = FUTEX_INITIALIZER;
82
 
82
 
83
/** Make a fast synchronous call.
83
/** Make a fast synchronous call.
84
 *
84
 *
85
 * Only one payload argument can be passed using this function. However, this
85
 * Only one payload argument can be passed using this function. However, this
86
 * function is faster than the generic ipc_call_sync_3().
86
 * function is faster than the generic ipc_call_sync_3().
87
 *
87
 *
88
 * @param phoneid   Phone handle for the call.
88
 * @param phoneid   Phone handle for the call.
89
 * @param method    Requested method.
89
 * @param method    Requested method.
90
 * @param arg1      Service-defined payload argument.
90
 * @param arg1      Service-defined payload argument.
91
 * @param result    If non-NULL, the return ARG1 will be stored there.
91
 * @param result    If non-NULL, the return ARG1 will be stored there.
92
 *
92
 *
93
 * @return      Negative values represent errors returned by IPC.
93
 * @return      Negative values represent errors returned by IPC.
94
 *          Otherwise the RETVAL of the answer is returned.
94
 *          Otherwise the RETVAL of the answer is returned.
95
 */
95
 */
96
int ipc_call_sync(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t *result)
96
int ipc_call_sync(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t *result)
97
{
97
{
98
    ipc_call_t resdata;
98
    ipc_call_t resdata;
99
    int callres;
99
    int callres;
100
   
100
   
101
    callres = __SYSCALL4(SYS_IPC_CALL_SYNC_FAST, phoneid, method, arg1,
101
    callres = __SYSCALL4(SYS_IPC_CALL_SYNC_FAST, phoneid, method, arg1,
102
        (sysarg_t) &resdata);
102
        (sysarg_t) &resdata);
103
    if (callres)
103
    if (callres)
104
        return callres;
104
        return callres;
105
    if (result)
105
    if (result)
106
        *result = IPC_GET_ARG1(resdata);
106
        *result = IPC_GET_ARG1(resdata);
107
    return IPC_GET_RETVAL(resdata);
107
    return IPC_GET_RETVAL(resdata);
108
}
108
}
109
 
109
 
110
/** Make a synchronous call transmitting 3 arguments of payload.
110
/** Make a synchronous call transmitting 3 arguments of payload.
111
 *
111
 *
112
 * @param phoneid   Phone handle for the call.
112
 * @param phoneid   Phone handle for the call.
113
 * @param method    Requested method.
113
 * @param method    Requested method.
114
 * @param arg1      Service-defined payload argument.
114
 * @param arg1      Service-defined payload argument.
115
 * @param arg2      Service-defined payload argument.
115
 * @param arg2      Service-defined payload argument.
116
 * @param arg3      Service-defined payload argument.
116
 * @param arg3      Service-defined payload argument.
117
 * @param result1   If non-NULL, storage for the first return argument.
117
 * @param result1   If non-NULL, storage for the first return argument.
118
 * @param result2   If non-NULL, storage for the second return argument.
118
 * @param result2   If non-NULL, storage for the second return argument.
119
 * @param result3   If non-NULL, storage for the third return argument.
119
 * @param result3   If non-NULL, storage for the third return argument.
120
 *
120
 *
121
 * @return      Negative value means IPC error.
121
 * @return      Negative value means IPC error.
122
 *          Otherwise the RETVAL of the answer.
122
 *          Otherwise the RETVAL of the answer.
123
 */
123
 */
124
int ipc_call_sync_3(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
124
int ipc_call_sync_3(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
125
    ipcarg_t arg3, ipcarg_t *result1, ipcarg_t *result2, ipcarg_t *result3)
125
    ipcarg_t arg3, ipcarg_t *result1, ipcarg_t *result2, ipcarg_t *result3)
126
{
126
{
127
    ipc_call_t data;
127
    ipc_call_t data;
128
    int callres;
128
    int callres;
129
 
129
 
130
    IPC_SET_METHOD(data, method);
130
    IPC_SET_METHOD(data, method);
131
    IPC_SET_ARG1(data, arg1);
131
    IPC_SET_ARG1(data, arg1);
132
    IPC_SET_ARG2(data, arg2);
132
    IPC_SET_ARG2(data, arg2);
133
    IPC_SET_ARG3(data, arg3);
133
    IPC_SET_ARG3(data, arg3);
134
 
134
 
135
    callres = __SYSCALL3(SYS_IPC_CALL_SYNC, phoneid, (sysarg_t) &data,
135
    callres = __SYSCALL3(SYS_IPC_CALL_SYNC, phoneid, (sysarg_t) &data,
136
        (sysarg_t) &data);
136
        (sysarg_t) &data);
137
    if (callres)
137
    if (callres)
138
        return callres;
138
        return callres;
139
 
139
 
140
    if (result1)
140
    if (result1)
141
        *result1 = IPC_GET_ARG1(data);
141
        *result1 = IPC_GET_ARG1(data);
142
    if (result2)
142
    if (result2)
143
        *result2 = IPC_GET_ARG2(data);
143
        *result2 = IPC_GET_ARG2(data);
144
    if (result3)
144
    if (result3)
145
        *result3 = IPC_GET_ARG3(data);
145
        *result3 = IPC_GET_ARG3(data);
146
    return IPC_GET_RETVAL(data);
146
    return IPC_GET_RETVAL(data);
147
}
147
}
148
 
148
 
149
/** Syscall to send asynchronous message.
149
/** Syscall to send asynchronous message.
150
 *
150
 *
151
 * @param phoneid   Phone handle for the call.
151
 * @param phoneid   Phone handle for the call.
152
 * @param data      Call data with the request.
152
 * @param data      Call data with the request.
153
 *
153
 *
154
 * @return      Hash of the call or an error code.
154
 * @return      Hash of the call or an error code.
155
 */
155
 */
156
static ipc_callid_t _ipc_call_async(int phoneid, ipc_call_t *data)
156
static ipc_callid_t _ipc_call_async(int phoneid, ipc_call_t *data)
157
{
157
{
158
    return __SYSCALL2(SYS_IPC_CALL_ASYNC, phoneid, (sysarg_t) data);
158
    return __SYSCALL2(SYS_IPC_CALL_ASYNC, phoneid, (sysarg_t) data);
159
}
159
}
160
 
160
 
161
/** Prolog to ipc_call_async_*() functions.
161
/** Prolog to ipc_call_async_*() functions.
162
 *
162
 *
163
 * @param private   Argument for the answer/error callback.
163
 * @param private   Argument for the answer/error callback.
164
 * @param callback  Answer/error callback.
164
 * @param callback  Answer/error callback.
165
 *
165
 *
166
 * @return      New, partially initialized async_call structure or NULL.
166
 * @return      New, partially initialized async_call structure or NULL.
167
 */
167
 */
168
static inline async_call_t *ipc_prepare_async(void *private,
168
static inline async_call_t *ipc_prepare_async(void *private,
169
    ipc_async_callback_t callback)
169
    ipc_async_callback_t callback)
170
{
170
{
171
    async_call_t *call;
171
    async_call_t *call;
172
 
172
 
173
    call = malloc(sizeof(*call));
173
    call = malloc(sizeof(*call));
174
    if (!call) {
174
    if (!call) {
175
        if (callback)
175
        if (callback)
176
            callback(private, ENOMEM, NULL);
176
            callback(private, ENOMEM, NULL);
177
        return NULL;
177
        return NULL;
178
    }
178
    }
179
    call->callback = callback;
179
    call->callback = callback;
180
    call->private = private;
180
    call->private = private;
181
 
181
 
182
    return call;
182
    return call;
183
}
183
}
184
 
184
 
185
/** Epilogue of ipc_call_async_*() functions.
185
/** Epilogue of ipc_call_async_*() functions.
186
 *
186
 *
187
 * @param callid    Value returned by the SYS_IPC_CALL_ASYNC_* syscall.
187
 * @param callid    Value returned by the SYS_IPC_CALL_ASYNC_* syscall.
188
 * @param phoneid   Phone handle through which the call was made.
188
 * @param phoneid   Phone handle through which the call was made.
189
 * @param call      async_call structure returned by ipc_prepare_async().
189
 * @param call      async_call structure returned by ipc_prepare_async().
190
 * @param can_preempt   If non-zero, the current pseudo thread can be preempted
190
 * @param can_preempt   If non-zero, the current pseudo thread can be preempted
191
 *          in this call.
191
 *          in this call.
192
 */
192
 */
193
static inline void ipc_finish_async(ipc_callid_t callid, int phoneid,
193
static inline void ipc_finish_async(ipc_callid_t callid, int phoneid,
194
    async_call_t *call, int can_preempt)
194
    async_call_t *call, int can_preempt)
195
{
195
{
196
    if (!call) { /* Nothing to do regardless if failed or not */
196
    if (!call) { /* Nothing to do regardless if failed or not */
197
        futex_up(&ipc_futex);
197
        futex_up(&ipc_futex);
198
        return;
198
        return;
199
    }
199
    }
200
 
200
 
201
    if (callid == IPC_CALLRET_FATAL) {
201
    if (callid == IPC_CALLRET_FATAL) {
202
        futex_up(&ipc_futex);
202
        futex_up(&ipc_futex);
203
        /* Call asynchronous handler with error code */
203
        /* Call asynchronous handler with error code */
204
        if (call->callback)
204
        if (call->callback)
205
            call->callback(call->private, ENOENT, NULL);
205
            call->callback(call->private, ENOENT, NULL);
206
        free(call);
206
        free(call);
207
        return;
207
        return;
208
    }
208
    }
209
 
209
 
210
    if (callid == IPC_CALLRET_TEMPORARY) {
210
    if (callid == IPC_CALLRET_TEMPORARY) {
211
        futex_up(&ipc_futex);
211
        futex_up(&ipc_futex);
212
 
212
 
213
        call->u.msg.phoneid = phoneid;
213
        call->u.msg.phoneid = phoneid;
214
       
214
       
215
        futex_down(&async_futex);
215
        futex_down(&async_futex);
216
        list_append(&call->list, &queued_calls);
216
        list_append(&call->list, &queued_calls);
217
 
217
 
218
        if (can_preempt) {
218
        if (can_preempt) {
219
            call->ptid = psthread_get_id();
219
            call->fid = fibril_get_id();
220
            psthread_schedule_next_adv(PS_TO_MANAGER);
220
            fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
221
            /* Async futex unlocked by previous call */
221
            /* Async futex unlocked by previous call */
222
        } else {
222
        } else {
223
            call->ptid = 0;
223
            call->fid = 0;
224
            futex_up(&async_futex);
224
            futex_up(&async_futex);
225
        }
225
        }
226
        return;
226
        return;
227
    }
227
    }
228
    call->u.callid = callid;
228
    call->u.callid = callid;
229
    /* Add call to the list of dispatched calls */
229
    /* Add call to the list of dispatched calls */
230
    list_append(&call->list, &dispatched_calls);
230
    list_append(&call->list, &dispatched_calls);
231
    futex_up(&ipc_futex);
231
    futex_up(&ipc_futex);
232
   
232
   
233
}
233
}
234
 
234
 
235
/** Make a fast asynchronous call.
235
/** Make a fast asynchronous call.
236
 *
236
 *
237
 * This function can only handle two arguments of payload. It is, however,
237
 * This function can only handle two arguments of payload. It is, however,
238
 * faster than the more generic ipc_call_async_3().
238
 * faster than the more generic ipc_call_async_3().
239
 *
239
 *
240
 * Note that this function is a void function.
240
 * Note that this function is a void function.
241
 * During normal opertation, answering this call will trigger the callback.
241
 * During normal opertation, answering this call will trigger the callback.
242
 * In case of fatal error, call the callback handler with the proper error code.
242
 * In case of fatal error, call the callback handler with the proper error code.
243
 * If the call cannot be temporarily made, queue it.
243
 * If the call cannot be temporarily made, queue it.
244
 *
244
 *
245
 * @param phoneid   Phone handle for the call.
245
 * @param phoneid   Phone handle for the call.
246
 * @param method    Requested method.
246
 * @param method    Requested method.
247
 * @param arg1      Service-defined payload argument.
247
 * @param arg1      Service-defined payload argument.
248
 * @param arg2      Service-defined payload argument.
248
 * @param arg2      Service-defined payload argument.
249
 * @param private   Argument to be passed to the answer/error callback.
249
 * @param private   Argument to be passed to the answer/error callback.
250
 * @param callback  Answer or error callback.
250
 * @param callback  Answer or error callback.
251
 * @param can_preempt   If non-zero, the current pseudo thread will be preempted
251
 * @param can_preempt   If non-zero, the current pseudo thread will be preempted
252
 *          in case the kernel temporarily refuses to accept more
252
 *          in case the kernel temporarily refuses to accept more
253
 *          asynchronous calls.
253
 *          asynchronous calls.
254
 */
254
 */
255
void ipc_call_async_2(int phoneid, ipcarg_t method, ipcarg_t arg1,
255
void ipc_call_async_2(int phoneid, ipcarg_t method, ipcarg_t arg1,
256
    ipcarg_t arg2, void *private, ipc_async_callback_t callback,
256
    ipcarg_t arg2, void *private, ipc_async_callback_t callback,
257
    int can_preempt)
257
    int can_preempt)
258
{
258
{
259
    async_call_t *call = NULL;
259
    async_call_t *call = NULL;
260
    ipc_callid_t callid;
260
    ipc_callid_t callid;
261
 
261
 
262
    if (callback) {
262
    if (callback) {
263
        call = ipc_prepare_async(private, callback);
263
        call = ipc_prepare_async(private, callback);
264
        if (!call)
264
        if (!call)
265
            return;
265
            return;
266
    }
266
    }
267
 
267
 
268
    /*
268
    /*
269
     * We need to make sure that we get callid before another thread
269
     * We need to make sure that we get callid before another thread
270
     * accesses the queue again.
270
     * accesses the queue again.
271
     */
271
     */
272
    futex_down(&ipc_futex);
272
    futex_down(&ipc_futex);
273
    callid = __SYSCALL4(SYS_IPC_CALL_ASYNC_FAST, phoneid, method, arg1,
273
    callid = __SYSCALL4(SYS_IPC_CALL_ASYNC_FAST, phoneid, method, arg1,
274
        arg2);
274
        arg2);
275
 
275
 
276
    if (callid == IPC_CALLRET_TEMPORARY) {
276
    if (callid == IPC_CALLRET_TEMPORARY) {
277
        if (!call) {
277
        if (!call) {
278
            call = ipc_prepare_async(private, callback);
278
            call = ipc_prepare_async(private, callback);
279
            if (!call)
279
            if (!call)
280
                return;
280
                return;
281
        }
281
        }
282
        IPC_SET_METHOD(call->u.msg.data, method);
282
        IPC_SET_METHOD(call->u.msg.data, method);
283
        IPC_SET_ARG1(call->u.msg.data, arg1);
283
        IPC_SET_ARG1(call->u.msg.data, arg1);
284
        IPC_SET_ARG2(call->u.msg.data, arg2);
284
        IPC_SET_ARG2(call->u.msg.data, arg2);
285
    }
285
    }
286
    ipc_finish_async(callid, phoneid, call, can_preempt);
286
    ipc_finish_async(callid, phoneid, call, can_preempt);
287
}
287
}
288
 
288
 
289
/** Make an asynchronous call transmitting the entire payload.
289
/** Make an asynchronous call transmitting the entire payload.
290
 *
290
 *
291
 * Note that this function is a void function.
291
 * Note that this function is a void function.
292
 * During normal opertation, answering this call will trigger the callback.
292
 * During normal opertation, answering this call will trigger the callback.
293
 * In case of fatal error, call the callback handler with the proper error code.
293
 * In case of fatal error, call the callback handler with the proper error code.
294
 * If the call cannot be temporarily made, queue it.
294
 * If the call cannot be temporarily made, queue it.
295
 *
295
 *
296
 * @param phoneid   Phone handle for the call.
296
 * @param phoneid   Phone handle for the call.
297
 * @param method    Requested method.
297
 * @param method    Requested method.
298
 * @param arg1      Service-defined payload argument.
298
 * @param arg1      Service-defined payload argument.
299
 * @param arg2      Service-defined payload argument.
299
 * @param arg2      Service-defined payload argument.
300
 * @param arg3      Service-defined payload argument.
300
 * @param arg3      Service-defined payload argument.
301
 * @param private   Argument to be passed to the answer/error callback.
301
 * @param private   Argument to be passed to the answer/error callback.
302
 * @param callback  Answer or error callback.
302
 * @param callback  Answer or error callback.
303
 * @param can_preempt   If non-zero, the current pseudo thread will be preempted
303
 * @param can_preempt   If non-zero, the current pseudo thread will be preempted
304
 *          in case the kernel temporarily refuses to accept more
304
 *          in case the kernel temporarily refuses to accept more
305
 *          asynchronous calls.
305
 *          asynchronous calls.
306
 *
306
 *
307
 */
307
 */
308
void ipc_call_async_3(int phoneid, ipcarg_t method, ipcarg_t arg1,
308
void ipc_call_async_3(int phoneid, ipcarg_t method, ipcarg_t arg1,
309
    ipcarg_t arg2, ipcarg_t arg3, void *private, ipc_async_callback_t callback,
309
    ipcarg_t arg2, ipcarg_t arg3, void *private, ipc_async_callback_t callback,
310
    int can_preempt)
310
    int can_preempt)
311
{
311
{
312
    async_call_t *call;
312
    async_call_t *call;
313
    ipc_callid_t callid;
313
    ipc_callid_t callid;
314
 
314
 
315
    call = ipc_prepare_async(private, callback);
315
    call = ipc_prepare_async(private, callback);
316
    if (!call)
316
    if (!call)
317
        return;
317
        return;
318
 
318
 
319
    IPC_SET_METHOD(call->u.msg.data, method);
319
    IPC_SET_METHOD(call->u.msg.data, method);
320
    IPC_SET_ARG1(call->u.msg.data, arg1);
320
    IPC_SET_ARG1(call->u.msg.data, arg1);
321
    IPC_SET_ARG2(call->u.msg.data, arg2);
321
    IPC_SET_ARG2(call->u.msg.data, arg2);
322
    IPC_SET_ARG3(call->u.msg.data, arg3);
322
    IPC_SET_ARG3(call->u.msg.data, arg3);
323
    /*
323
    /*
324
     * We need to make sure that we get callid before another thread accesses
324
     * We need to make sure that we get callid before another thread accesses
325
     * the queue again.
325
     * the queue again.
326
     */
326
     */
327
    futex_down(&ipc_futex);
327
    futex_down(&ipc_futex);
328
    callid = _ipc_call_async(phoneid, &call->u.msg.data);
328
    callid = _ipc_call_async(phoneid, &call->u.msg.data);
329
 
329
 
330
    ipc_finish_async(callid, phoneid, call, can_preempt);
330
    ipc_finish_async(callid, phoneid, call, can_preempt);
331
}
331
}
332
 
332
 
333
 
333
 
334
/** Answer a received call - fast version.
334
/** Answer a received call - fast version.
335
 *
335
 *
336
 * The fast answer makes use of passing retval and first two arguments in
336
 * The fast answer makes use of passing retval and first two arguments in
337
 * registers. If you need to return more, use the ipc_answer() instead.
337
 * registers. If you need to return more, use the ipc_answer() instead.
338
 *
338
 *
339
 * @param callid    Hash of the call being answered.
339
 * @param callid    Hash of the call being answered.
340
 * @param retval    Return value.
340
 * @param retval    Return value.
341
 * @param arg1      First return argument.
341
 * @param arg1      First return argument.
342
 * @param arg2      Second return argument.
342
 * @param arg2      Second return argument.
343
 *
343
 *
344
 * @return      Zero on success or a value from @ref errno.h on failure.
344
 * @return      Zero on success or a value from @ref errno.h on failure.
345
 */
345
 */
346
ipcarg_t ipc_answer_fast(ipc_callid_t callid, ipcarg_t retval, ipcarg_t arg1,
346
ipcarg_t ipc_answer_fast(ipc_callid_t callid, ipcarg_t retval, ipcarg_t arg1,
347
    ipcarg_t arg2)
347
    ipcarg_t arg2)
348
{
348
{
349
    return __SYSCALL4(SYS_IPC_ANSWER_FAST, callid, retval, arg1, arg2);
349
    return __SYSCALL4(SYS_IPC_ANSWER_FAST, callid, retval, arg1, arg2);
350
}
350
}
351
 
351
 
352
/** Answer a received call - full version.
352
/** Answer a received call - full version.
353
 *
353
 *
354
 * @param callid    Hash of the call being answered.
354
 * @param callid    Hash of the call being answered.
355
 * @param call      Call structure with the answer.
355
 * @param call      Call structure with the answer.
356
 *          Must be already initialized by the responder.
356
 *          Must be already initialized by the responder.
357
 *
357
 *
358
 * @return      Zero on success or a value from @ref errno.h on failure.
358
 * @return      Zero on success or a value from @ref errno.h on failure.
359
 */
359
 */
360
ipcarg_t ipc_answer(ipc_callid_t callid, ipc_call_t *call)
360
ipcarg_t ipc_answer(ipc_callid_t callid, ipc_call_t *call)
361
{
361
{
362
    return __SYSCALL2(SYS_IPC_ANSWER, callid, (sysarg_t) call);
362
    return __SYSCALL2(SYS_IPC_ANSWER, callid, (sysarg_t) call);
363
}
363
}
364
 
364
 
365
 
365
 
366
/** Try to dispatch queued calls from the async queue. */
366
/** Try to dispatch queued calls from the async queue. */
367
static void try_dispatch_queued_calls(void)
367
static void try_dispatch_queued_calls(void)
368
{
368
{
369
    async_call_t *call;
369
    async_call_t *call;
370
    ipc_callid_t callid;
370
    ipc_callid_t callid;
371
 
371
 
372
    /** @todo
372
    /** @todo
373
     * Integrate intelligently ipc_futex, so that it is locked during
373
     * Integrate intelligently ipc_futex, so that it is locked during
374
     * ipc_call_async_*(), until it is added to dispatched_calls.
374
     * ipc_call_async_*(), until it is added to dispatched_calls.
375
     */
375
     */
376
    futex_down(&async_futex);
376
    futex_down(&async_futex);
377
    while (!list_empty(&queued_calls)) {
377
    while (!list_empty(&queued_calls)) {
378
        call = list_get_instance(queued_calls.next, async_call_t, list);
378
        call = list_get_instance(queued_calls.next, async_call_t, list);
379
        callid = _ipc_call_async(call->u.msg.phoneid, &call->u.msg.data);
379
        callid = _ipc_call_async(call->u.msg.phoneid, &call->u.msg.data);
380
        if (callid == IPC_CALLRET_TEMPORARY) {
380
        if (callid == IPC_CALLRET_TEMPORARY) {
381
            break;
381
            break;
382
        }
382
        }
383
        list_remove(&call->list);
383
        list_remove(&call->list);
384
 
384
 
385
        futex_up(&async_futex);
385
        futex_up(&async_futex);
386
        if (call->ptid)
386
        if (call->fid)
387
            psthread_add_ready(call->ptid);
387
            fibril_add_ready(call->fid);
388
       
388
       
389
        if (callid == IPC_CALLRET_FATAL) {
389
        if (callid == IPC_CALLRET_FATAL) {
390
            if (call->callback)
390
            if (call->callback)
391
                call->callback(call->private, ENOENT, NULL);
391
                call->callback(call->private, ENOENT, NULL);
392
            free(call);
392
            free(call);
393
        } else {
393
        } else {
394
            call->u.callid = callid;
394
            call->u.callid = callid;
395
            futex_down(&ipc_futex);
395
            futex_down(&ipc_futex);
396
            list_append(&call->list, &dispatched_calls);
396
            list_append(&call->list, &dispatched_calls);
397
            futex_up(&ipc_futex);
397
            futex_up(&ipc_futex);
398
        }
398
        }
399
        futex_down(&async_futex);
399
        futex_down(&async_futex);
400
    }
400
    }
401
    futex_up(&async_futex);
401
    futex_up(&async_futex);
402
}
402
}
403
 
403
 
404
/** Handle a received answer.
404
/** Handle a received answer.
405
 *
405
 *
406
 * Find the hash of the answer and call the answer callback.
406
 * Find the hash of the answer and call the answer callback.
407
 *
407
 *
408
 * @todo Make it use hash table.
408
 * @todo Make it use hash table.
409
 *
409
 *
410
 * @param callid    Hash of the received answer.
410
 * @param callid    Hash of the received answer.
411
 *          The answer has the same hash as the request OR'ed with
411
 *          The answer has the same hash as the request OR'ed with
412
 *          the IPC_CALLID_ANSWERED bit.
412
 *          the IPC_CALLID_ANSWERED bit.
413
 * @param data      Call data of the answer.
413
 * @param data      Call data of the answer.
414
 */
414
 */
415
static void handle_answer(ipc_callid_t callid, ipc_call_t *data)
415
static void handle_answer(ipc_callid_t callid, ipc_call_t *data)
416
{
416
{
417
    link_t *item;
417
    link_t *item;
418
    async_call_t *call;
418
    async_call_t *call;
419
 
419
 
420
    callid &= ~IPC_CALLID_ANSWERED;
420
    callid &= ~IPC_CALLID_ANSWERED;
421
   
421
   
422
    futex_down(&ipc_futex);
422
    futex_down(&ipc_futex);
423
    for (item = dispatched_calls.next; item != &dispatched_calls;
423
    for (item = dispatched_calls.next; item != &dispatched_calls;
424
        item = item->next) {
424
        item = item->next) {
425
        call = list_get_instance(item, async_call_t, list);
425
        call = list_get_instance(item, async_call_t, list);
426
        if (call->u.callid == callid) {
426
        if (call->u.callid == callid) {
427
            list_remove(&call->list);
427
            list_remove(&call->list);
428
            futex_up(&ipc_futex);
428
            futex_up(&ipc_futex);
429
            if (call->callback)
429
            if (call->callback)
430
                call->callback(call->private,
430
                call->callback(call->private,
431
                    IPC_GET_RETVAL(*data), data);
431
                    IPC_GET_RETVAL(*data), data);
432
            free(call);
432
            free(call);
433
            return;
433
            return;
434
        }
434
        }
435
    }
435
    }
436
    futex_up(&ipc_futex);
436
    futex_up(&ipc_futex);
437
}
437
}
438
 
438
 
439
 
439
 
440
/** Wait for a first call to come.
440
/** Wait for a first call to come.
441
 *
441
 *
442
 * @param call      Storage where the incoming call data will be stored.
442
 * @param call      Storage where the incoming call data will be stored.
443
 * @param usec      Timeout in microseconds
443
 * @param usec      Timeout in microseconds
444
 * @param flags     Flags passed to SYS_IPC_WAIT (blocking, nonblocking).
444
 * @param flags     Flags passed to SYS_IPC_WAIT (blocking, nonblocking).
445
 *
445
 *
446
 * @return      Hash of the call. Note that certain bits have special
446
 * @return      Hash of the call. Note that certain bits have special
447
 *          meaning. IPC_CALLID_ANSWERED will be set in an answer
447
 *          meaning. IPC_CALLID_ANSWERED will be set in an answer
448
 *          and IPC_CALLID_NOTIFICATION is used for notifications.
448
 *          and IPC_CALLID_NOTIFICATION is used for notifications.
449
 *         
449
 *         
450
 */
450
 */
451
ipc_callid_t ipc_wait_cycle(ipc_call_t *call, uint32_t usec, int flags)
451
ipc_callid_t ipc_wait_cycle(ipc_call_t *call, uint32_t usec, int flags)
452
{
452
{
453
    ipc_callid_t callid;
453
    ipc_callid_t callid;
454
 
454
 
455
    callid = __SYSCALL3(SYS_IPC_WAIT, (sysarg_t) call, usec, flags);
455
    callid = __SYSCALL3(SYS_IPC_WAIT, (sysarg_t) call, usec, flags);
456
    /* Handle received answers */
456
    /* Handle received answers */
457
    if (callid & IPC_CALLID_ANSWERED) {
457
    if (callid & IPC_CALLID_ANSWERED) {
458
        handle_answer(callid, call);
458
        handle_answer(callid, call);
459
        try_dispatch_queued_calls();
459
        try_dispatch_queued_calls();
460
    }
460
    }
461
 
461
 
462
    return callid;
462
    return callid;
463
}
463
}
464
 
464
 
465
/** Wait some time for an IPC call.
465
/** Wait some time for an IPC call.
466
 *
466
 *
467
 * The call will return after an answer is received.
467
 * The call will return after an answer is received.
468
 *
468
 *
469
 * @param call      Storage where the incoming call data will be stored.
469
 * @param call      Storage where the incoming call data will be stored.
470
 * @param usec      Timeout in microseconds.
470
 * @param usec      Timeout in microseconds.
471
 *
471
 *
472
 * @return      Hash of the answer.
472
 * @return      Hash of the answer.
473
 */
473
 */
474
ipc_callid_t ipc_wait_for_call_timeout(ipc_call_t *call, uint32_t usec)
474
ipc_callid_t ipc_wait_for_call_timeout(ipc_call_t *call, uint32_t usec)
475
{
475
{
476
    ipc_callid_t callid;
476
    ipc_callid_t callid;
477
 
477
 
478
    do {
478
    do {
479
        callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE);
479
        callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE);
480
    } while (callid & IPC_CALLID_ANSWERED);
480
    } while (callid & IPC_CALLID_ANSWERED);
481
 
481
 
482
    return callid;
482
    return callid;
483
}
483
}
484
 
484
 
485
/** Check if there is an IPC call waiting to be picked up.
485
/** Check if there is an IPC call waiting to be picked up.
486
 *
486
 *
487
 * @param call      Storage where the incoming call will be stored.
487
 * @param call      Storage where the incoming call will be stored.
488
 * @return      Hash of the answer.
488
 * @return      Hash of the answer.
489
 */
489
 */
490
ipc_callid_t ipc_trywait_for_call(ipc_call_t *call)
490
ipc_callid_t ipc_trywait_for_call(ipc_call_t *call)
491
{
491
{
492
    ipc_callid_t callid;
492
    ipc_callid_t callid;
493
 
493
 
494
    do {
494
    do {
495
        callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT,
495
        callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT,
496
            SYNCH_FLAGS_NON_BLOCKING);
496
            SYNCH_FLAGS_NON_BLOCKING);
497
    } while (callid & IPC_CALLID_ANSWERED);
497
    } while (callid & IPC_CALLID_ANSWERED);
498
 
498
 
499
    return callid;
499
    return callid;
500
}
500
}
501
 
501
 
502
/** Ask destination to do a callback connection.
502
/** Ask destination to do a callback connection.
503
 *
503
 *
504
 * @param phoneid   Phone handle used for contacting the other side.
504
 * @param phoneid   Phone handle used for contacting the other side.
505
 * @param arg1      Service-defined argument.
505
 * @param arg1      Service-defined argument.
506
 * @param arg2      Service-defined argument.
506
 * @param arg2      Service-defined argument.
507
 * @param phonehash Storage where the library will store an opaque
507
 * @param phonehash Storage where the library will store an opaque
508
 *          identifier of the phone that will be used for incoming
508
 *          identifier of the phone that will be used for incoming
509
 *          calls. This identifier can be used for connection
509
 *          calls. This identifier can be used for connection
510
 *          tracking.
510
 *          tracking.
511
 *
511
 *
512
 * @return      Zero on success or a negative error code.
512
 * @return      Zero on success or a negative error code.
513
 */
513
 */
514
int ipc_connect_to_me(int phoneid, int arg1, int arg2, ipcarg_t *phonehash)
514
int ipc_connect_to_me(int phoneid, int arg1, int arg2, ipcarg_t *phonehash)
515
{
515
{
516
    return ipc_call_sync_3(phoneid, IPC_M_CONNECT_TO_ME, arg1, arg2, 0, 0, 0,
516
    return ipc_call_sync_3(phoneid, IPC_M_CONNECT_TO_ME, arg1, arg2, 0, 0, 0,
517
        phonehash);
517
        phonehash);
518
}
518
}
519
 
519
 
520
/** Ask through phone for a new connection to some service.
520
/** Ask through phone for a new connection to some service.
521
 *
521
 *
522
 * @param phoneid   Phone handle used for contacting the other side.
522
 * @param phoneid   Phone handle used for contacting the other side.
523
 * @param arg1      User defined argument.
523
 * @param arg1      User defined argument.
524
 * @param arg2      User defined argument.
524
 * @param arg2      User defined argument.
525
 *
525
 *
526
 * @return      New phone handle on success or a negative error code.
526
 * @return      New phone handle on success or a negative error code.
527
 */
527
 */
528
int ipc_connect_me_to(int phoneid, int arg1, int arg2)
528
int ipc_connect_me_to(int phoneid, int arg1, int arg2)
529
{
529
{
530
    ipcarg_t newphid;
530
    ipcarg_t newphid;
531
    int res;
531
    int res;
532
 
532
 
533
    res =  ipc_call_sync_3(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, 0, 0, 0,
533
    res =  ipc_call_sync_3(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, 0, 0, 0,
534
        &newphid);
534
        &newphid);
535
    if (res)
535
    if (res)
536
        return res;
536
        return res;
537
    return newphid;
537
    return newphid;
538
}
538
}
539
 
539
 
540
/** Hang up a phone.
540
/** Hang up a phone.
541
 *
541
 *
542
 * @param phoneid   Handle of the phone to be hung up.
542
 * @param phoneid   Handle of the phone to be hung up.
543
 *
543
 *
544
 * @return      Zero on success or a negative error code.
544
 * @return      Zero on success or a negative error code.
545
 */
545
 */
546
int ipc_hangup(int phoneid)
546
int ipc_hangup(int phoneid)
547
{
547
{
548
    return __SYSCALL1(SYS_IPC_HANGUP, phoneid);
548
    return __SYSCALL1(SYS_IPC_HANGUP, phoneid);
549
}
549
}
550
 
550
 
551
/** Register IRQ notification.
551
/** Register IRQ notification.
552
 *
552
 *
553
 * @param inr       IRQ number.
553
 * @param inr       IRQ number.
554
 * @param devno     Device number of the device generating inr.
554
 * @param devno     Device number of the device generating inr.
555
 * @param method    Use this method for notifying me.
555
 * @param method    Use this method for notifying me.
556
 * @param ucode     Top-half pseudocode handler.
556
 * @param ucode     Top-half pseudocode handler.
557
 *
557
 *
558
 * @return      Value returned by the kernel.
558
 * @return      Value returned by the kernel.
559
 */
559
 */
560
int ipc_register_irq(int inr, int devno, int method, irq_code_t *ucode)
560
int ipc_register_irq(int inr, int devno, int method, irq_code_t *ucode)
561
{
561
{
562
    return __SYSCALL4(SYS_IPC_REGISTER_IRQ, inr, devno, method,
562
    return __SYSCALL4(SYS_IPC_REGISTER_IRQ, inr, devno, method,
563
        (sysarg_t) ucode);
563
        (sysarg_t) ucode);
564
}
564
}
565
 
565
 
566
/** Unregister IRQ notification.
566
/** Unregister IRQ notification.
567
 *
567
 *
568
 * @param inr       IRQ number.
568
 * @param inr       IRQ number.
569
 * @param devno     Device number of the device generating inr.
569
 * @param devno     Device number of the device generating inr.
570
 *
570
 *
571
 * @return      Value returned by the kernel.
571
 * @return      Value returned by the kernel.
572
 */
572
 */
573
int ipc_unregister_irq(int inr, int devno)
573
int ipc_unregister_irq(int inr, int devno)
574
{
574
{
575
    return __SYSCALL2(SYS_IPC_UNREGISTER_IRQ, inr, devno);
575
    return __SYSCALL2(SYS_IPC_UNREGISTER_IRQ, inr, devno);
576
}
576
}
577
 
577
 
578
/** Forward a received call to another destination.
578
/** Forward a received call to another destination.
579
 *
579
 *
580
 * @param callid    Hash of the call to forward.
580
 * @param callid    Hash of the call to forward.
581
 * @param phoneid   Phone handle to use for forwarding.
581
 * @param phoneid   Phone handle to use for forwarding.
582
 * @param method    New method for the forwarded call.
582
 * @param method    New method for the forwarded call.
583
 * @param arg1      New value of the first argument for the forwarded call.
583
 * @param arg1      New value of the first argument for the forwarded call.
584
 *
584
 *
585
 * @return      Zero on success or an error code.
585
 * @return      Zero on success or an error code.
586
 *
586
 *
587
 * For non-system methods, the old method and arg1 are rewritten by the new
587
 * For non-system methods, the old method and arg1 are rewritten by the new
588
 * values. For system methods, the new method and arg1 are written to the old
588
 * values. For system methods, the new method and arg1 are written to the old
589
 * arg1 and arg2, respectivelly.
589
 * arg1 and arg2, respectivelly.
590
 */
590
 */
591
int ipc_forward_fast(ipc_callid_t callid, int phoneid, int method, ipcarg_t arg1)
591
int ipc_forward_fast(ipc_callid_t callid, int phoneid, int method, ipcarg_t arg1)
592
{
592
{
593
    return __SYSCALL4(SYS_IPC_FORWARD_FAST, callid, phoneid, method, arg1);
593
    return __SYSCALL4(SYS_IPC_FORWARD_FAST, callid, phoneid, method, arg1);
594
}
594
}
595
 
595
 
596
/** @}
596
/** @}
597
 */
597
 */
598
 
598