Subversion Repositories HelenOS

Rev

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

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