Subversion Repositories HelenOS

Rev

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

Rev 3037 Rev 3042
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 genericipc
29
/** @addtogroup genericipc
30
 * @{
30
 * @{
31
 */
31
 */
32
/** @file
32
/** @file
33
 */
33
 */
34
 
34
 
35
/* Lock ordering
35
/* Lock ordering
36
 *
36
 *
37
 * First the answerbox, then the phone.
37
 * First the answerbox, then the phone.
38
 */
38
 */
39
 
39
 
-
 
40
#include <synch/synch.h>
40
#include <synch/spinlock.h>
41
#include <synch/spinlock.h>
-
 
42
#include <synch/mutex.h>
41
#include <synch/waitq.h>
43
#include <synch/waitq.h>
42
#include <synch/synch.h>
44
#include <synch/synch.h>
43
#include <ipc/ipc.h>
45
#include <ipc/ipc.h>
44
#include <errno.h>
46
#include <errno.h>
45
#include <mm/slab.h>
47
#include <mm/slab.h>
46
#include <arch.h>
48
#include <arch.h>
47
#include <proc/task.h>
49
#include <proc/task.h>
48
#include <memstr.h>
50
#include <memstr.h>
49
#include <debug.h>
51
#include <debug.h>
50
 
52
 
51
#include <print.h>
53
#include <print.h>
52
#include <console/klog.h>
54
#include <console/klog.h>
53
#include <proc/thread.h>
55
#include <proc/thread.h>
54
#include <arch/interrupt.h>
56
#include <arch/interrupt.h>
55
#include <ipc/irq.h>
57
#include <ipc/irq.h>
56
 
58
 
57
/** Open channel that is assigned automatically to new tasks */
59
/** Open channel that is assigned automatically to new tasks */
58
answerbox_t *ipc_phone_0 = NULL;
60
answerbox_t *ipc_phone_0 = NULL;
59
 
61
 
60
static slab_cache_t *ipc_call_slab;
62
static slab_cache_t *ipc_call_slab;
61
 
63
 
62
/** Initialize a call structure.
64
/** Initialize a call structure.
63
 *
65
 *
64
 * @param call      Call structure to be initialized.
66
 * @param call      Call structure to be initialized.
65
 */
67
 */
66
static void _ipc_call_init(call_t *call)
68
static void _ipc_call_init(call_t *call)
67
{
69
{
68
    memsetb((uintptr_t) call, sizeof(*call), 0);
70
    memsetb((uintptr_t) call, sizeof(*call), 0);
69
    call->callerbox = &TASK->answerbox;
71
    call->callerbox = &TASK->answerbox;
70
    call->sender = TASK;
72
    call->sender = TASK;
71
    call->buffer = NULL;
73
    call->buffer = NULL;
72
}
74
}
73
 
75
 
74
/** Allocate and initialize a call structure.
76
/** Allocate and initialize a call structure.
75
 *
77
 *
76
 * The call is initialized, so that the reply will be directed to
78
 * The call is initialized, so that the reply will be directed to
77
 * TASK->answerbox.
79
 * TASK->answerbox.
78
 *
80
 *
79
 * @param flags     Parameters for slab_alloc (e.g FRAME_ATOMIC).
81
 * @param flags     Parameters for slab_alloc (e.g FRAME_ATOMIC).
80
 *
82
 *
81
 * @return      If flags permit it, return NULL, or initialized kernel
83
 * @return      If flags permit it, return NULL, or initialized kernel
82
 *          call structure.
84
 *          call structure.
83
 */
85
 */
84
call_t *ipc_call_alloc(int flags)
86
call_t *ipc_call_alloc(int flags)
85
{
87
{
86
    call_t *call;
88
    call_t *call;
87
 
89
 
88
    call = slab_alloc(ipc_call_slab, flags);
90
    call = slab_alloc(ipc_call_slab, flags);
89
    _ipc_call_init(call);
91
    _ipc_call_init(call);
90
 
92
 
91
    return call;
93
    return call;
92
}
94
}
93
 
95
 
94
/** Initialize a statically allocated call structure.
96
/** Initialize a statically allocated call structure.
95
 *
97
 *
96
 * @param call      Statically allocated kernel call structure to be
98
 * @param call      Statically allocated kernel call structure to be
97
 *          initialized.
99
 *          initialized.
98
 */
100
 */
99
void ipc_call_static_init(call_t *call)
101
void ipc_call_static_init(call_t *call)
100
{
102
{
101
    _ipc_call_init(call);
103
    _ipc_call_init(call);
102
    call->flags |= IPC_CALL_STATIC_ALLOC;
104
    call->flags |= IPC_CALL_STATIC_ALLOC;
103
}
105
}
104
 
106
 
105
/** Deallocate a call structure.
107
/** Deallocate a call structure.
106
 *
108
 *
107
 * @param call      Call structure to be freed.
109
 * @param call      Call structure to be freed.
108
 */
110
 */
109
void ipc_call_free(call_t *call)
111
void ipc_call_free(call_t *call)
110
{
112
{
111
    ASSERT(!(call->flags & IPC_CALL_STATIC_ALLOC));
113
    ASSERT(!(call->flags & IPC_CALL_STATIC_ALLOC));
112
    /* Check to see if we have data in the IPC_M_DATA_SEND buffer. */
114
    /* Check to see if we have data in the IPC_M_DATA_SEND buffer. */
113
    if (call->buffer)
115
    if (call->buffer)
114
        free(call->buffer);
116
        free(call->buffer);
115
    slab_free(ipc_call_slab, call);
117
    slab_free(ipc_call_slab, call);
116
}
118
}
117
 
119
 
118
/** Initialize an answerbox structure.
120
/** Initialize an answerbox structure.
119
 *
121
 *
120
 * @param box       Answerbox structure to be initialized.
122
 * @param box       Answerbox structure to be initialized.
121
 * @param task      Task to which the answerbox belongs.
123
 * @param task      Task to which the answerbox belongs.
122
 */
124
 */
123
void ipc_answerbox_init(answerbox_t *box, task_t *task)
125
void ipc_answerbox_init(answerbox_t *box, task_t *task)
124
{
126
{
125
    spinlock_initialize(&box->lock, "ipc_box_lock");
127
    spinlock_initialize(&box->lock, "ipc_box_lock");
126
    spinlock_initialize(&box->irq_lock, "ipc_box_irqlock");
128
    spinlock_initialize(&box->irq_lock, "ipc_box_irqlock");
127
    waitq_initialize(&box->wq);
129
    waitq_initialize(&box->wq);
128
    list_initialize(&box->connected_phones);
130
    list_initialize(&box->connected_phones);
129
    list_initialize(&box->calls);
131
    list_initialize(&box->calls);
130
    list_initialize(&box->dispatched_calls);
132
    list_initialize(&box->dispatched_calls);
131
    list_initialize(&box->answers);
133
    list_initialize(&box->answers);
132
    list_initialize(&box->irq_notifs);
134
    list_initialize(&box->irq_notifs);
133
    list_initialize(&box->irq_head);
135
    list_initialize(&box->irq_head);
134
    box->task = task;
136
    box->task = task;
135
}
137
}
136
 
138
 
137
/** Connect a phone to an answerbox.
139
/** Connect a phone to an answerbox.
138
 *
140
 *
139
 * @param phone     Initialized phone structure.
141
 * @param phone     Initialized phone structure.
140
 * @param box       Initialized answerbox structure.
142
 * @param box       Initialized answerbox structure.
141
 */
143
 */
142
void ipc_phone_connect(phone_t *phone, answerbox_t *box)
144
void ipc_phone_connect(phone_t *phone, answerbox_t *box)
143
{
145
{
144
    spinlock_lock(&phone->lock);
146
    mutex_lock(&phone->lock);
145
 
147
 
146
    phone->state = IPC_PHONE_CONNECTED;
148
    phone->state = IPC_PHONE_CONNECTED;
147
    phone->callee = box;
149
    phone->callee = box;
148
 
150
 
149
    spinlock_lock(&box->lock);
151
    spinlock_lock(&box->lock);
150
    list_append(&phone->link, &box->connected_phones);
152
    list_append(&phone->link, &box->connected_phones);
151
    spinlock_unlock(&box->lock);
153
    spinlock_unlock(&box->lock);
152
 
154
 
153
    spinlock_unlock(&phone->lock);
155
    mutex_unlock(&phone->lock);
154
}
156
}
155
 
157
 
156
/** Initialize a phone structure.
158
/** Initialize a phone structure.
157
 *
159
 *
158
 * @param phone     Phone structure to be initialized.
160
 * @param phone     Phone structure to be initialized.
159
 */
161
 */
160
void ipc_phone_init(phone_t *phone)
162
void ipc_phone_init(phone_t *phone)
161
{
163
{
162
    spinlock_initialize(&phone->lock, "phone_lock");
164
    mutex_initialize(&phone->lock);
163
    phone->callee = NULL;
165
    phone->callee = NULL;
164
    phone->state = IPC_PHONE_FREE;
166
    phone->state = IPC_PHONE_FREE;
165
    atomic_set(&phone->active_calls, 0);
167
    atomic_set(&phone->active_calls, 0);
166
}
168
}
167
 
169
 
168
/** Helper function to facilitate synchronous calls.
170
/** Helper function to facilitate synchronous calls.
169
 *
171
 *
170
 * @param phone     Destination kernel phone structure.
172
 * @param phone     Destination kernel phone structure.
171
 * @param request   Call structure with request.
173
 * @param request   Call structure with request.
172
 */
174
 */
173
void ipc_call_sync(phone_t *phone, call_t *request)
175
void ipc_call_sync(phone_t *phone, call_t *request)
174
{
176
{
175
    answerbox_t sync_box;
177
    answerbox_t sync_box;
176
 
178
 
177
    ipc_answerbox_init(&sync_box, TASK);
179
    ipc_answerbox_init(&sync_box, TASK);
178
 
180
 
179
    /* We will receive data in a special box. */
181
    /* We will receive data in a special box. */
180
    request->callerbox = &sync_box;
182
    request->callerbox = &sync_box;
181
 
183
 
182
    ipc_call(phone, request);
184
    ipc_call(phone, request);
183
    ipc_wait_for_call(&sync_box, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE);
185
    ipc_wait_for_call(&sync_box, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE);
184
}
186
}
185
 
187
 
186
/** Answer a message which was not dispatched and is not listed in any queue.
188
/** Answer a message which was not dispatched and is not listed in any queue.
187
 *
189
 *
188
 * @param call      Call structure to be answered.
190
 * @param call      Call structure to be answered.
189
 */
191
 */
190
static void _ipc_answer_free_call(call_t *call)
192
static void _ipc_answer_free_call(call_t *call)
191
{
193
{
192
    answerbox_t *callerbox = call->callerbox;
194
    answerbox_t *callerbox = call->callerbox;
193
 
195
 
194
    call->flags |= IPC_CALL_ANSWERED;
196
    call->flags |= IPC_CALL_ANSWERED;
195
 
197
 
196
    spinlock_lock(&callerbox->lock);
198
    spinlock_lock(&callerbox->lock);
197
    list_append(&call->link, &callerbox->answers);
199
    list_append(&call->link, &callerbox->answers);
198
    spinlock_unlock(&callerbox->lock);
200
    spinlock_unlock(&callerbox->lock);
199
    waitq_wakeup(&callerbox->wq, WAKEUP_FIRST);
201
    waitq_wakeup(&callerbox->wq, WAKEUP_FIRST);
200
}
202
}
201
 
203
 
202
/** Answer a message which is in a callee queue.
204
/** Answer a message which is in a callee queue.
203
 *
205
 *
204
 * @param box       Answerbox that is answering the message.
206
 * @param box       Answerbox that is answering the message.
205
 * @param call      Modified request that is being sent back.
207
 * @param call      Modified request that is being sent back.
206
 */
208
 */
207
void ipc_answer(answerbox_t *box, call_t *call)
209
void ipc_answer(answerbox_t *box, call_t *call)
208
{
210
{
209
    /* Remove from active box */
211
    /* Remove from active box */
210
    spinlock_lock(&box->lock);
212
    spinlock_lock(&box->lock);
211
    list_remove(&call->link);
213
    list_remove(&call->link);
212
    spinlock_unlock(&box->lock);
214
    spinlock_unlock(&box->lock);
213
    /* Send back answer */
215
    /* Send back answer */
214
    _ipc_answer_free_call(call);
216
    _ipc_answer_free_call(call);
215
}
217
}
216
 
218
 
217
/** Simulate sending back a message.
219
/** Simulate sending back a message.
218
 *
220
 *
219
 * Most errors are better handled by forming a normal backward
221
 * Most errors are better handled by forming a normal backward
220
 * message and sending it as a normal answer.
222
 * message and sending it as a normal answer.
221
 *
223
 *
222
 * @param phone     Phone structure the call should appear to come from.
224
 * @param phone     Phone structure the call should appear to come from.
223
 * @param call      Call structure to be answered.
225
 * @param call      Call structure to be answered.
224
 * @param err       Return value to be used for the answer.
226
 * @param err       Return value to be used for the answer.
225
 */
227
 */
226
void ipc_backsend_err(phone_t *phone, call_t *call, unative_t err)
228
void ipc_backsend_err(phone_t *phone, call_t *call, unative_t err)
227
{
229
{
228
    call->data.phone = phone;
230
    call->data.phone = phone;
229
    atomic_inc(&phone->active_calls);
231
    atomic_inc(&phone->active_calls);
230
    IPC_SET_RETVAL(call->data, err);
232
    IPC_SET_RETVAL(call->data, err);
231
    _ipc_answer_free_call(call);
233
    _ipc_answer_free_call(call);
232
}
234
}
233
 
235
 
234
/** Unsafe unchecking version of ipc_call.
236
/** Unsafe unchecking version of ipc_call.
235
 *
237
 *
236
 * @param phone     Phone structure the call comes from.
238
 * @param phone     Phone structure the call comes from.
237
 * @param box       Destination answerbox structure.
239
 * @param box       Destination answerbox structure.
238
 * @param call      Call structure with request.
240
 * @param call      Call structure with request.
239
 */
241
 */
240
static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
242
static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
241
{
243
{
242
    if (!(call->flags & IPC_CALL_FORWARDED)) {
244
    if (!(call->flags & IPC_CALL_FORWARDED)) {
243
        atomic_inc(&phone->active_calls);
245
        atomic_inc(&phone->active_calls);
244
        call->data.phone = phone;
246
        call->data.phone = phone;
245
    }
247
    }
246
 
248
 
247
    spinlock_lock(&box->lock);
249
    spinlock_lock(&box->lock);
248
    list_append(&call->link, &box->calls);
250
    list_append(&call->link, &box->calls);
249
    spinlock_unlock(&box->lock);
251
    spinlock_unlock(&box->lock);
250
    waitq_wakeup(&box->wq, WAKEUP_FIRST);
252
    waitq_wakeup(&box->wq, WAKEUP_FIRST);
251
}
253
}
252
 
254
 
253
/** Send an asynchronous request using a phone to an answerbox.
255
/** Send an asynchronous request using a phone to an answerbox.
254
 *
256
 *
255
 * @param phone     Phone structure the call comes from and which is
257
 * @param phone     Phone structure the call comes from and which is
256
 *          connected to the destination answerbox.
258
 *          connected to the destination answerbox.
257
 * @param call      Call structure with request.
259
 * @param call      Call structure with request.
258
 *
260
 *
259
 * @return      Return 0 on success, ENOENT on error.
261
 * @return      Return 0 on success, ENOENT on error.
260
 */
262
 */
261
int ipc_call(phone_t *phone, call_t *call)
263
int ipc_call(phone_t *phone, call_t *call)
262
{
264
{
263
    answerbox_t *box;
265
    answerbox_t *box;
264
 
266
 
265
    spinlock_lock(&phone->lock);
267
    mutex_lock(&phone->lock);
266
    if (phone->state != IPC_PHONE_CONNECTED) {
268
    if (phone->state != IPC_PHONE_CONNECTED) {
267
        spinlock_unlock(&phone->lock);
269
        mutex_unlock(&phone->lock);
268
        if (call->flags & IPC_CALL_FORWARDED) {
270
        if (call->flags & IPC_CALL_FORWARDED) {
269
            IPC_SET_RETVAL(call->data, EFORWARD);
271
            IPC_SET_RETVAL(call->data, EFORWARD);
270
            _ipc_answer_free_call(call);
272
            _ipc_answer_free_call(call);
271
        } else {
273
        } else {
272
            if (phone->state == IPC_PHONE_HUNGUP)
274
            if (phone->state == IPC_PHONE_HUNGUP)
273
                ipc_backsend_err(phone, call, EHANGUP);
275
                ipc_backsend_err(phone, call, EHANGUP);
274
            else
276
            else
275
                ipc_backsend_err(phone, call, ENOENT);
277
                ipc_backsend_err(phone, call, ENOENT);
276
        }
278
        }
277
        return ENOENT;
279
        return ENOENT;
278
    }
280
    }
279
    box = phone->callee;
281
    box = phone->callee;
280
    _ipc_call(phone, box, call);
282
    _ipc_call(phone, box, call);
281
   
283
   
282
    spinlock_unlock(&phone->lock);
284
    mutex_unlock(&phone->lock);
283
    return 0;
285
    return 0;
284
}
286
}
285
 
287
 
286
/** Disconnect phone from answerbox.
288
/** Disconnect phone from answerbox.
287
 *
289
 *
288
 * This call leaves the phone in the HUNGUP state. The change to 'free' is done
290
 * This call leaves the phone in the HUNGUP state. The change to 'free' is done
289
 * lazily later.
291
 * lazily later.
290
 *
292
 *
291
 * @param phone     Phone structure to be hung up.
293
 * @param phone     Phone structure to be hung up.
292
 *              
294
 *              
293
 * @return      Return 0 if the phone is disconnected.
295
 * @return      Return 0 if the phone is disconnected.
294
 *          Return -1 if the phone was already disconnected.
296
 *          Return -1 if the phone was already disconnected.
295
 */
297
 */
296
int ipc_phone_hangup(phone_t *phone)
298
int ipc_phone_hangup(phone_t *phone)
297
{
299
{
298
    answerbox_t *box;
300
    answerbox_t *box;
299
    call_t *call;
301
    call_t *call;
300
   
302
   
301
    spinlock_lock(&phone->lock);
303
    mutex_lock(&phone->lock);
302
    if (phone->state == IPC_PHONE_FREE ||
304
    if (phone->state == IPC_PHONE_FREE ||
303
        phone->state == IPC_PHONE_HUNGUP ||
305
        phone->state == IPC_PHONE_HUNGUP ||
304
        phone->state == IPC_PHONE_CONNECTING) {
306
        phone->state == IPC_PHONE_CONNECTING) {
305
        spinlock_unlock(&phone->lock);
307
        mutex_unlock(&phone->lock);
306
        return -1;
308
        return -1;
307
    }
309
    }
308
    box = phone->callee;
310
    box = phone->callee;
309
    if (phone->state != IPC_PHONE_SLAMMED) {
311
    if (phone->state != IPC_PHONE_SLAMMED) {
310
        /* Remove myself from answerbox */
312
        /* Remove myself from answerbox */
311
        spinlock_lock(&box->lock);
313
        spinlock_lock(&box->lock);
312
        list_remove(&phone->link);
314
        list_remove(&phone->link);
313
        spinlock_unlock(&box->lock);
315
        spinlock_unlock(&box->lock);
314
 
316
 
315
        if (phone->state != IPC_PHONE_SLAMMED) {
317
        if (phone->state != IPC_PHONE_SLAMMED) {
316
            call = ipc_call_alloc(0);
318
            call = ipc_call_alloc(0);
317
            IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
319
            IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
318
            call->flags |= IPC_CALL_DISCARD_ANSWER;
320
            call->flags |= IPC_CALL_DISCARD_ANSWER;
319
            _ipc_call(phone, box, call);
321
            _ipc_call(phone, box, call);
320
        }
322
        }
321
    }
323
    }
322
 
324
 
323
    phone->state = IPC_PHONE_HUNGUP;
325
    phone->state = IPC_PHONE_HUNGUP;
324
    spinlock_unlock(&phone->lock);
326
    mutex_unlock(&phone->lock);
325
 
327
 
326
    return 0;
328
    return 0;
327
}
329
}
328
 
330
 
329
/** Forwards call from one answerbox to another one.
331
/** Forwards call from one answerbox to another one.
330
 *
332
 *
331
 * @param call      Call structure to be redirected.
333
 * @param call      Call structure to be redirected.
332
 * @param newphone  Phone structure to target answerbox.
334
 * @param newphone  Phone structure to target answerbox.
333
 * @param oldbox    Old answerbox structure.
335
 * @param oldbox    Old answerbox structure.
334
 * @param mode      Flags that specify mode of the forward operation.
336
 * @param mode      Flags that specify mode of the forward operation.
335
 *
337
 *
336
 * @return      Return 0 if forwarding succeeded or an error code if
338
 * @return      Return 0 if forwarding succeeded or an error code if
337
 *          there was error.
339
 *          there was error.
338
 *
340
 *
339
 * The return value serves only as an information for the forwarder,
341
 * The return value serves only as an information for the forwarder,
340
 * the original caller is notified automatically with EFORWARD.
342
 * the original caller is notified automatically with EFORWARD.
341
 */
343
 */
342
int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox, int mode)
344
int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox, int mode)
343
{
345
{
344
    spinlock_lock(&oldbox->lock);
346
    spinlock_lock(&oldbox->lock);
345
    list_remove(&call->link);
347
    list_remove(&call->link);
346
    spinlock_unlock(&oldbox->lock);
348
    spinlock_unlock(&oldbox->lock);
347
 
349
 
348
    if (mode & IPC_FF_ROUTE_FROM_ME)
350
    if (mode & IPC_FF_ROUTE_FROM_ME)
349
        call->data.phone = newphone;
351
        call->data.phone = newphone;
350
 
352
 
351
    return ipc_call(newphone, call);
353
    return ipc_call(newphone, call);
352
}
354
}
353
 
355
 
354
 
356
 
355
/** Wait for a phone call.
357
/** Wait for a phone call.
356
 *
358
 *
357
 * @param box       Answerbox expecting the call.
359
 * @param box       Answerbox expecting the call.
358
 * @param usec      Timeout in microseconds. See documentation for
360
 * @param usec      Timeout in microseconds. See documentation for
359
 *          waitq_sleep_timeout() for decription of its special
361
 *          waitq_sleep_timeout() for decription of its special
360
 *          meaning.
362
 *          meaning.
361
 * @param flags     Select mode of sleep operation. See documentation for
363
 * @param flags     Select mode of sleep operation. See documentation for
362
 *          waitq_sleep_timeout() for description of its special
364
 *          waitq_sleep_timeout() for description of its special
363
 *          meaning.
365
 *          meaning.
364
 * @return      Recived call structure or NULL.
366
 * @return      Recived call structure or NULL.
365
 *
367
 *
366
 * To distinguish between a call and an answer, have a look at call->flags.
368
 * To distinguish between a call and an answer, have a look at call->flags.
367
 */
369
 */
368
call_t *ipc_wait_for_call(answerbox_t *box, uint32_t usec, int flags)
370
call_t *ipc_wait_for_call(answerbox_t *box, uint32_t usec, int flags)
369
{
371
{
370
    call_t *request;
372
    call_t *request;
371
    ipl_t ipl;
373
    ipl_t ipl;
372
    int rc;
374
    int rc;
373
 
375
 
374
restart:
376
restart:
375
    rc = waitq_sleep_timeout(&box->wq, usec, flags);
377
    rc = waitq_sleep_timeout(&box->wq, usec, flags);
376
    if (SYNCH_FAILED(rc))
378
    if (SYNCH_FAILED(rc))
377
        return NULL;
379
        return NULL;
378
   
380
   
379
    spinlock_lock(&box->lock);
381
    spinlock_lock(&box->lock);
380
    if (!list_empty(&box->irq_notifs)) {
382
    if (!list_empty(&box->irq_notifs)) {
381
        ipl = interrupts_disable();
383
        ipl = interrupts_disable();
382
        spinlock_lock(&box->irq_lock);
384
        spinlock_lock(&box->irq_lock);
383
 
385
 
384
        request = list_get_instance(box->irq_notifs.next, call_t, link);
386
        request = list_get_instance(box->irq_notifs.next, call_t, link);
385
        list_remove(&request->link);
387
        list_remove(&request->link);
386
 
388
 
387
        spinlock_unlock(&box->irq_lock);
389
        spinlock_unlock(&box->irq_lock);
388
        interrupts_restore(ipl);
390
        interrupts_restore(ipl);
389
    } else if (!list_empty(&box->answers)) {
391
    } else if (!list_empty(&box->answers)) {
390
        /* Handle asynchronous answers */
392
        /* Handle asynchronous answers */
391
        request = list_get_instance(box->answers.next, call_t, link);
393
        request = list_get_instance(box->answers.next, call_t, link);
392
        list_remove(&request->link);
394
        list_remove(&request->link);
393
        atomic_dec(&request->data.phone->active_calls);
395
        atomic_dec(&request->data.phone->active_calls);
394
    } else if (!list_empty(&box->calls)) {
396
    } else if (!list_empty(&box->calls)) {
395
        /* Handle requests */
397
        /* Handle requests */
396
        request = list_get_instance(box->calls.next, call_t, link);
398
        request = list_get_instance(box->calls.next, call_t, link);
397
        list_remove(&request->link);
399
        list_remove(&request->link);
398
        /* Append request to dispatch queue */
400
        /* Append request to dispatch queue */
399
        list_append(&request->link, &box->dispatched_calls);
401
        list_append(&request->link, &box->dispatched_calls);
400
    } else {
402
    } else {
401
        /* This can happen regularly after ipc_cleanup */
403
        /* This can happen regularly after ipc_cleanup */
402
        spinlock_unlock(&box->lock);
404
        spinlock_unlock(&box->lock);
403
        goto restart;
405
        goto restart;
404
    }
406
    }
405
    spinlock_unlock(&box->lock);
407
    spinlock_unlock(&box->lock);
406
    return request;
408
    return request;
407
}
409
}
408
 
410
 
409
/** Answer all calls from list with EHANGUP answer.
411
/** Answer all calls from list with EHANGUP answer.
410
 *
412
 *
411
 * @param lst       Head of the list to be cleaned up.
413
 * @param lst       Head of the list to be cleaned up.
412
 */
414
 */
413
static void ipc_cleanup_call_list(link_t *lst)
415
static void ipc_cleanup_call_list(link_t *lst)
414
{
416
{
415
    call_t *call;
417
    call_t *call;
416
 
418
 
417
    while (!list_empty(lst)) {
419
    while (!list_empty(lst)) {
418
        call = list_get_instance(lst->next, call_t, link);
420
        call = list_get_instance(lst->next, call_t, link);
419
        if (call->buffer)
421
        if (call->buffer)
420
            free(call->buffer);
422
            free(call->buffer);
421
        list_remove(&call->link);
423
        list_remove(&call->link);
422
 
424
 
423
        IPC_SET_RETVAL(call->data, EHANGUP);
425
        IPC_SET_RETVAL(call->data, EHANGUP);
424
        _ipc_answer_free_call(call);
426
        _ipc_answer_free_call(call);
425
    }
427
    }
426
}
428
}
427
 
429
 
428
/** Disconnects all phones connected to an answerbox.
430
/** Disconnects all phones connected to an answerbox.
429
 *
431
 *
430
 * @param box       Answerbox to disconnect phones from.
432
 * @param box       Answerbox to disconnect phones from.
431
 * @param notify_box    If true, the answerbox will get a hangup message for
433
 * @param notify_box    If true, the answerbox will get a hangup message for
432
 *          each disconnected phone.
434
 *          each disconnected phone.
433
 */
435
 */
434
static void ipc_answerbox_slam_phones(answerbox_t *box, bool notify_box)
436
static void ipc_answerbox_slam_phones(answerbox_t *box, bool notify_box)
435
{
437
{
436
    phone_t *phone;
438
    phone_t *phone;
437
    DEADLOCK_PROBE_INIT(p_phonelck);
439
    DEADLOCK_PROBE_INIT(p_phonelck);
438
    ipl_t ipl;
440
    ipl_t ipl;
439
    call_t *call;
441
    call_t *call;
440
 
442
 
441
    call = ipc_call_alloc(0);
443
    call = ipc_call_alloc(0);
442
 
444
 
443
    /* Disconnect all phones connected to our answerbox */
445
    /* Disconnect all phones connected to our answerbox */
444
restart_phones:
446
restart_phones:
445
    ipl = interrupts_disable();
447
    ipl = interrupts_disable();
446
    spinlock_lock(&box->lock);
448
    spinlock_lock(&box->lock);
447
    while (!list_empty(&box->connected_phones)) {
449
    while (!list_empty(&box->connected_phones)) {
448
        phone = list_get_instance(box->connected_phones.next,
450
        phone = list_get_instance(box->connected_phones.next,
449
            phone_t, link);
451
            phone_t, link);
450
        if (!spinlock_trylock(&phone->lock)) {
452
        if (SYNCH_FAILED(mutex_trylock(&phone->lock))) {
451
            spinlock_unlock(&box->lock);
453
            spinlock_unlock(&box->lock);
452
            interrupts_restore(ipl);
454
            interrupts_restore(ipl);
453
            DEADLOCK_PROBE(p_phonelck, DEADLOCK_THRESHOLD);
455
            DEADLOCK_PROBE(p_phonelck, DEADLOCK_THRESHOLD);
454
            goto restart_phones;
456
            goto restart_phones;
455
        }
457
        }
456
       
458
       
457
        /* Disconnect phone */
459
        /* Disconnect phone */
458
        ASSERT(phone->state == IPC_PHONE_CONNECTED);
460
        ASSERT(phone->state == IPC_PHONE_CONNECTED);
459
 
461
 
460
        list_remove(&phone->link);
462
        list_remove(&phone->link);
461
        phone->state = IPC_PHONE_SLAMMED;
463
        phone->state = IPC_PHONE_SLAMMED;
462
 
464
 
463
        if (notify_box) {
465
        if (notify_box) {
464
            spinlock_unlock(&phone->lock);
466
            mutex_unlock(&phone->lock);
465
            spinlock_unlock(&box->lock);
467
            spinlock_unlock(&box->lock);
466
            interrupts_restore(ipl);
468
            interrupts_restore(ipl);
467
 
469
 
468
            /*
470
            /*
469
             * Send one message to the answerbox for each
471
             * Send one message to the answerbox for each
470
             * phone. Used to make sure the kbox thread
472
             * phone. Used to make sure the kbox thread
471
             * wakes up after the last phone has been
473
             * wakes up after the last phone has been
472
             * disconnected.
474
             * disconnected.
473
             */
475
             */
474
            IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
476
            IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
475
            call->flags |= IPC_CALL_DISCARD_ANSWER;
477
            call->flags |= IPC_CALL_DISCARD_ANSWER;
476
            _ipc_call(phone, box, call);
478
            _ipc_call(phone, box, call);
477
 
479
 
478
            /* Allocate another call in advance */
480
            /* Allocate another call in advance */
479
            call = ipc_call_alloc(0);
481
            call = ipc_call_alloc(0);
480
 
482
 
481
            /* Must start again */
483
            /* Must start again */
482
            goto restart_phones;
484
            goto restart_phones;
483
        }
485
        }
484
 
486
 
485
        spinlock_unlock(&phone->lock);
487
        mutex_unlock(&phone->lock);
486
    }
488
    }
487
 
489
 
488
    spinlock_unlock(&box->lock);
490
    spinlock_unlock(&box->lock);
489
    interrupts_restore(ipl);
491
    interrupts_restore(ipl);
490
 
492
 
491
    /* Free unused call */
493
    /* Free unused call */
492
    if (call) ipc_call_free(call);
494
    if (call) ipc_call_free(call);
493
}
495
}
494
 
496
 
495
static void ipc_kbox_cleanup()
497
static void ipc_kbox_cleanup()
496
{
498
{
497
    bool have_kb_thread;
499
    bool have_kb_thread;
498
 
500
 
499
    /* Only hold kb_cleanup_lock while setting kb_finished - this is enough */
501
    /* Only hold kb_cleanup_lock while setting kb_finished - this is enough */
500
    mutex_lock(&TASK->kb_cleanup_lock);
502
    mutex_lock(&TASK->kb_cleanup_lock);
501
    TASK->kb_finished = true;
503
    TASK->kb_finished = true;
502
    mutex_unlock(&TASK->kb_cleanup_lock);
504
    mutex_unlock(&TASK->kb_cleanup_lock);
503
 
505
 
504
    have_kb_thread = (TASK->kb_thread != NULL);
506
    have_kb_thread = (TASK->kb_thread != NULL);
505
 
507
 
506
    /* From now on nobody will try to connect phones or attach kbox threads */
508
    /* From now on nobody will try to connect phones or attach kbox threads */
507
 
509
 
508
    /*
510
    /*
509
     * Disconnect all phones connected to our kbox. Passing true for
511
     * Disconnect all phones connected to our kbox. Passing true for
510
     * notify_box causes a HANGUP message to be inserted for each
512
     * notify_box causes a HANGUP message to be inserted for each
511
     * disconnected phone. This ensures the kbox thread is going to
513
     * disconnected phone. This ensures the kbox thread is going to
512
     * wake up and terminate.
514
     * wake up and terminate.
513
     */
515
     */
514
    ipc_answerbox_slam_phones(&TASK->kernel_box, have_kb_thread);
516
    ipc_answerbox_slam_phones(&TASK->kernel_box, have_kb_thread);
515
   
517
   
516
    if (have_kb_thread) {
518
    if (have_kb_thread) {
517
        klog_printf("join kb_thread..");
519
        klog_printf("join kb_thread..");
518
        thread_join(TASK->kb_thread);
520
        thread_join(TASK->kb_thread);
519
        thread_detach(TASK->kb_thread);
521
        thread_detach(TASK->kb_thread);
520
        klog_printf("join done");
522
        klog_printf("join done");
521
        TASK->kb_thread = NULL;
523
        TASK->kb_thread = NULL;
522
    }
524
    }
523
 
525
 
524
    /* Answer all messages in 'calls' and 'dispatched_calls' queues */
526
    /* Answer all messages in 'calls' and 'dispatched_calls' queues */
525
    spinlock_lock(&TASK->kernel_box.lock);
527
    spinlock_lock(&TASK->kernel_box.lock);
526
    ipc_cleanup_call_list(&TASK->kernel_box.dispatched_calls);
528
    ipc_cleanup_call_list(&TASK->kernel_box.dispatched_calls);
527
    ipc_cleanup_call_list(&TASK->kernel_box.calls);
529
    ipc_cleanup_call_list(&TASK->kernel_box.calls);
528
    spinlock_unlock(&TASK->kernel_box.lock);
530
    spinlock_unlock(&TASK->kernel_box.lock);
529
}
531
}
530
 
532
 
531
 
533
 
532
/** Cleans up all IPC communication of the current task.
534
/** Cleans up all IPC communication of the current task.
533
 *
535
 *
534
 * Note: ipc_hangup sets returning answerbox to TASK->answerbox, you
536
 * Note: ipc_hangup sets returning answerbox to TASK->answerbox, you
535
 * have to change it as well if you want to cleanup other tasks than TASK.
537
 * have to change it as well if you want to cleanup other tasks than TASK.
536
 */
538
 */
537
void ipc_cleanup(void)
539
void ipc_cleanup(void)
538
{
540
{
539
    int i;
541
    int i;
540
    call_t *call;
542
    call_t *call;
541
 
543
 
542
    /* Disconnect all our phones ('ipc_phone_hangup') */
544
    /* Disconnect all our phones ('ipc_phone_hangup') */
543
    for (i = 0; i < IPC_MAX_PHONES; i++)
545
    for (i = 0; i < IPC_MAX_PHONES; i++)
544
        ipc_phone_hangup(&TASK->phones[i]);
546
        ipc_phone_hangup(&TASK->phones[i]);
545
 
547
 
546
    /* Disconnect all connected irqs */
548
    /* Disconnect all connected irqs */
547
    ipc_irq_cleanup(&TASK->answerbox);
549
    ipc_irq_cleanup(&TASK->answerbox);
548
 
550
 
549
    /* Disconnect all phones connected to our regular answerbox */
551
    /* Disconnect all phones connected to our regular answerbox */
550
    ipc_answerbox_slam_phones(&TASK->answerbox, false);
552
    ipc_answerbox_slam_phones(&TASK->answerbox, false);
551
 
553
 
552
    /* Clean up kbox thread and communications */
554
    /* Clean up kbox thread and communications */
553
    ipc_kbox_cleanup();
555
    ipc_kbox_cleanup();
554
 
556
 
555
    /* Answer all messages in 'calls' and 'dispatched_calls' queues */
557
    /* Answer all messages in 'calls' and 'dispatched_calls' queues */
556
    spinlock_lock(&TASK->answerbox.lock);
558
    spinlock_lock(&TASK->answerbox.lock);
557
    ipc_cleanup_call_list(&TASK->answerbox.dispatched_calls);
559
    ipc_cleanup_call_list(&TASK->answerbox.dispatched_calls);
558
    ipc_cleanup_call_list(&TASK->answerbox.calls);
560
    ipc_cleanup_call_list(&TASK->answerbox.calls);
559
    spinlock_unlock(&TASK->answerbox.lock);
561
    spinlock_unlock(&TASK->answerbox.lock);
560
   
562
   
561
    /* Wait for all async answers to arrive */
563
    /* Wait for all async answers to arrive */
562
    while (1) {
564
    while (1) {
563
        /* Go through all phones, until all are FREE... */
565
        /* Go through all phones, until all are FREE... */
564
        /* Locking not needed, no one else should modify
566
        /* Locking not needed, no one else should modify
565
         * it, when we are in cleanup */
567
         * it, when we are in cleanup */
566
        for (i = 0; i < IPC_MAX_PHONES; i++) {
568
        for (i = 0; i < IPC_MAX_PHONES; i++) {
567
            if (TASK->phones[i].state == IPC_PHONE_HUNGUP &&
569
            if (TASK->phones[i].state == IPC_PHONE_HUNGUP &&
568
                atomic_get(&TASK->phones[i].active_calls) == 0)
570
                atomic_get(&TASK->phones[i].active_calls) == 0)
569
                TASK->phones[i].state = IPC_PHONE_FREE;
571
                TASK->phones[i].state = IPC_PHONE_FREE;
570
           
572
           
571
            /* Just for sure, we might have had some
573
            /* Just for sure, we might have had some
572
             * IPC_PHONE_CONNECTING phones */
574
             * IPC_PHONE_CONNECTING phones */
573
            if (TASK->phones[i].state == IPC_PHONE_CONNECTED)
575
            if (TASK->phones[i].state == IPC_PHONE_CONNECTED)
574
                ipc_phone_hangup(&TASK->phones[i]);
576
                ipc_phone_hangup(&TASK->phones[i]);
575
            /* If the hangup succeeded, it has sent a HANGUP
577
            /* If the hangup succeeded, it has sent a HANGUP
576
             * message, the IPC is now in HUNGUP state, we
578
             * message, the IPC is now in HUNGUP state, we
577
             * wait for the reply to come */
579
             * wait for the reply to come */
578
           
580
           
579
            if (TASK->phones[i].state != IPC_PHONE_FREE)
581
            if (TASK->phones[i].state != IPC_PHONE_FREE)
580
                break;
582
                break;
581
        }
583
        }
582
        /* Voila, got into cleanup */
584
        /* Voila, got into cleanup */
583
        if (i == IPC_MAX_PHONES)
585
        if (i == IPC_MAX_PHONES)
584
            break;
586
            break;
585
       
587
       
586
        call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
588
        call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
587
            SYNCH_FLAGS_NONE);
589
            SYNCH_FLAGS_NONE);
588
        ASSERT((call->flags & IPC_CALL_ANSWERED) ||
590
        ASSERT((call->flags & IPC_CALL_ANSWERED) ||
589
            (call->flags & IPC_CALL_NOTIF));
591
            (call->flags & IPC_CALL_NOTIF));
590
        ASSERT(!(call->flags & IPC_CALL_STATIC_ALLOC));
592
        ASSERT(!(call->flags & IPC_CALL_STATIC_ALLOC));
591
       
593
       
592
        atomic_dec(&TASK->active_calls);
594
        atomic_dec(&TASK->active_calls);
593
        ipc_call_free(call);
595
        ipc_call_free(call);
594
    }
596
    }
595
}
597
}
596
 
598
 
597
 
599
 
598
/** Initilize IPC subsystem */
600
/** Initilize IPC subsystem */
599
void ipc_init(void)
601
void ipc_init(void)
600
{
602
{
601
    ipc_call_slab = slab_cache_create("ipc_call", sizeof(call_t), 0, NULL,
603
    ipc_call_slab = slab_cache_create("ipc_call", sizeof(call_t), 0, NULL,
602
        NULL, 0);
604
        NULL, 0);
603
}
605
}
604
 
606
 
605
 
607
 
606
/** List answerbox contents.
608
/** List answerbox contents.
607
 *
609
 *
608
 * @param taskid    Task ID.
610
 * @param taskid    Task ID.
609
 */
611
 */
610
void ipc_print_task(task_id_t taskid)
612
void ipc_print_task(task_id_t taskid)
611
{
613
{
612
    task_t *task;
614
    task_t *task;
613
    int i;
615
    int i;
614
    call_t *call;
616
    call_t *call;
615
    link_t *tmp;
617
    link_t *tmp;
616
   
618
   
617
    spinlock_lock(&tasks_lock);
619
    spinlock_lock(&tasks_lock);
618
    task = task_find_by_id(taskid);
620
    task = task_find_by_id(taskid);
619
    if (task)
621
    if (task)
620
        spinlock_lock(&task->lock);
622
        spinlock_lock(&task->lock);
621
    spinlock_unlock(&tasks_lock);
623
    spinlock_unlock(&tasks_lock);
622
    if (!task)
624
    if (!task)
623
        return;
625
        return;
624
 
626
 
625
    /* Print opened phones & details */
627
    /* Print opened phones & details */
626
    printf("PHONE:\n");
628
    printf("PHONE:\n");
627
    for (i = 0; i < IPC_MAX_PHONES; i++) {
629
    for (i = 0; i < IPC_MAX_PHONES; i++) {
628
        spinlock_lock(&task->phones[i].lock);
630
        if (SYNCH_FAILED(mutex_trylock(&task->phones[i].lock))) {
-
 
631
            printf("%d: mutex busy\n", i);
-
 
632
            continue;
-
 
633
        }
629
        if (task->phones[i].state != IPC_PHONE_FREE) {
634
        if (task->phones[i].state != IPC_PHONE_FREE) {
630
            printf("%d: ", i);
635
            printf("%d: ", i);
631
            switch (task->phones[i].state) {
636
            switch (task->phones[i].state) {
632
            case IPC_PHONE_CONNECTING:
637
            case IPC_PHONE_CONNECTING:
633
                printf("connecting ");
638
                printf("connecting ");
634
                break;
639
                break;
635
            case IPC_PHONE_CONNECTED:
640
            case IPC_PHONE_CONNECTED:
636
                printf("connected to: %p ",
641
                printf("connected to: %p ",
637
                       task->phones[i].callee);
642
                       task->phones[i].callee);
638
                break;
643
                break;
639
            case IPC_PHONE_SLAMMED:
644
            case IPC_PHONE_SLAMMED:
640
                printf("slammed by: %p ",
645
                printf("slammed by: %p ",
641
                       task->phones[i].callee);
646
                       task->phones[i].callee);
642
                break;
647
                break;
643
            case IPC_PHONE_HUNGUP:
648
            case IPC_PHONE_HUNGUP:
644
                printf("hung up - was: %p ",
649
                printf("hung up - was: %p ",
645
                       task->phones[i].callee);
650
                       task->phones[i].callee);
646
                break;
651
                break;
647
            default:
652
            default:
648
                break;
653
                break;
649
            }
654
            }
650
            printf("active: %d\n",
655
            printf("active: %d\n",
651
                atomic_get(&task->phones[i].active_calls));
656
                atomic_get(&task->phones[i].active_calls));
652
        }
657
        }
653
        spinlock_unlock(&task->phones[i].lock);
658
        mutex_unlock(&task->phones[i].lock);
654
    }
659
    }
655
 
660
 
656
 
661
 
657
    /* Print answerbox - calls */
662
    /* Print answerbox - calls */
658
    spinlock_lock(&task->answerbox.lock);
663
    spinlock_lock(&task->answerbox.lock);
659
    printf("ABOX - CALLS:\n");
664
    printf("ABOX - CALLS:\n");
660
    for (tmp = task->answerbox.calls.next; tmp != &task->answerbox.calls;
665
    for (tmp = task->answerbox.calls.next; tmp != &task->answerbox.calls;
661
        tmp = tmp->next) {
666
        tmp = tmp->next) {
662
        call = list_get_instance(tmp, call_t, link);
667
        call = list_get_instance(tmp, call_t, link);
663
        printf("Callid: %p Srctask:%llu M:%d A1:%d A2:%d A3:%d "
668
        printf("Callid: %p Srctask:%llu M:%d A1:%d A2:%d A3:%d "
664
            "A4:%d A5:%d Flags:%x\n", call, call->sender->taskid,
669
            "A4:%d A5:%d Flags:%x\n", call, call->sender->taskid,
665
            IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
670
            IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
666
            IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
671
            IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
667
            IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
672
            IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
668
            call->flags);
673
            call->flags);
669
    }
674
    }
670
    /* Print answerbox - calls */
675
    /* Print answerbox - calls */
671
    printf("ABOX - DISPATCHED CALLS:\n");
676
    printf("ABOX - DISPATCHED CALLS:\n");
672
    for (tmp=task->answerbox.dispatched_calls.next;
677
    for (tmp = task->answerbox.dispatched_calls.next;
673
         tmp != &task->answerbox.dispatched_calls;
678
         tmp != &task->answerbox.dispatched_calls;
674
         tmp = tmp->next) {
679
         tmp = tmp->next) {
675
        call = list_get_instance(tmp, call_t, link);
680
        call = list_get_instance(tmp, call_t, link);
676
        printf("Callid: %p Srctask:%llu M:%d A1:%d A2:%d A3:%d "
681
        printf("Callid: %p Srctask:%llu M:%d A1:%d A2:%d A3:%d "
677
            "A4:%d A5:%d Flags:%x\n", call, call->sender->taskid,
682
            "A4:%d A5:%d Flags:%x\n", call, call->sender->taskid,
678
            IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
683
            IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
679
            IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
684
            IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
680
            IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
685
            IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
681
            call->flags);
686
            call->flags);
682
    }
687
    }
683
    /* Print answerbox - calls */
688
    /* Print answerbox - calls */
684
    printf("ABOX - ANSWERS:\n");
689
    printf("ABOX - ANSWERS:\n");
685
    for (tmp = task->answerbox.answers.next; tmp != &task->answerbox.answers;
690
    for (tmp = task->answerbox.answers.next; tmp != &task->answerbox.answers;
686
        tmp = tmp->next) {
691
        tmp = tmp->next) {
687
        call = list_get_instance(tmp, call_t, link);
692
        call = list_get_instance(tmp, call_t, link);
688
        printf("Callid:%p M:%d A1:%d A2:%d A3:%d A4:%d A5:%d Flags:%x\n",
693
        printf("Callid:%p M:%d A1:%d A2:%d A3:%d A4:%d A5:%d Flags:%x\n",
689
            call, IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
694
            call, IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
690
            IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
695
            IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
691
            IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
696
            IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
692
            call->flags);
697
            call->flags);
693
    }
698
    }
694
 
699
 
695
    spinlock_unlock(&task->answerbox.lock);
700
    spinlock_unlock(&task->answerbox.lock);
696
    spinlock_unlock(&task->lock);
701
    spinlock_unlock(&task->lock);
697
}
702
}
698
 
703
 
699
#include <ipc/ipcrsc.h>
704
#include <ipc/ipcrsc.h>
700
#include <console/klog.h>
705
#include <console/klog.h>
701
#include <udebug/udebug_ipc.h>
706
#include <udebug/udebug_ipc.h>
702
 
707
 
703
static void kbox_thread_proc(void *arg)
708
static void kbox_thread_proc(void *arg)
704
{
709
{
705
    call_t *call;
710
    call_t *call;
706
    int method;
711
    int method;
707
    bool done;
712
    bool done;
708
    ipl_t ipl;
713
    ipl_t ipl;
709
 
714
 
710
    (void)arg;
715
    (void)arg;
711
    klog_printf("kbox_thread_proc()");
716
    klog_printf("kbox_thread_proc()");
712
    done = false;
717
    done = false;
713
 
718
 
714
    while (!done) {
719
    while (!done) {
715
        //klog_printf("kbox: wait for call");
720
        //klog_printf("kbox: wait for call");
716
        call = ipc_wait_for_call(&TASK->kernel_box, SYNCH_NO_TIMEOUT,
721
        call = ipc_wait_for_call(&TASK->kernel_box, SYNCH_NO_TIMEOUT,
717
            SYNCH_FLAGS_NONE);
722
            SYNCH_FLAGS_NONE);
718
 
723
 
719
        if (call != NULL) {
724
        if (call != NULL) {
720
            method = IPC_GET_METHOD(call->data);
725
            method = IPC_GET_METHOD(call->data);
721
 
726
 
722
            if (method == IPC_M_DEBUG_ALL) {
727
            if (method == IPC_M_DEBUG_ALL) {
723
                udebug_call_receive(call);
728
                udebug_call_receive(call);
724
            }
729
            }
725
 
730
 
726
            if (method == IPC_M_PHONE_HUNGUP) {
731
            if (method == IPC_M_PHONE_HUNGUP) {
727
                klog_printf("kbox: handle hangup message");
732
                klog_printf("kbox: handle hangup message");
728
 
733
 
729
                /* Was it our debugger, who hung up? */
734
                /* Was it our debugger, who hung up? */
730
                if (call->sender == TASK->udebug.debugger) {
735
                if (call->sender == TASK->udebug.debugger) {
731
                    /* Terminate debugging session (if any) */
736
                    /* Terminate debugging session (if any) */
732
                    klog_printf("kbox: terminate debug session");
737
                    klog_printf("kbox: terminate debug session");
733
                    ipl = interrupts_disable();
738
                    ipl = interrupts_disable();
734
                    spinlock_lock(&TASK->lock);
739
                    spinlock_lock(&TASK->lock);
735
                    udebug_task_cleanup(TASK);
740
                    udebug_task_cleanup(TASK);
736
                    spinlock_unlock(&TASK->lock);
741
                    spinlock_unlock(&TASK->lock);
737
                    interrupts_restore(ipl);
742
                    interrupts_restore(ipl);
738
                } else {
743
                } else {
739
                    klog_printf("kbox: was not debugger");
744
                    klog_printf("kbox: was not debugger");
740
                }
745
                }
741
 
746
 
742
                klog_printf("kbox: continue with hangup message");
747
                klog_printf("kbox: continue with hangup message");
743
                IPC_SET_RETVAL(call->data, 0);
748
                IPC_SET_RETVAL(call->data, 0);
744
                ipc_answer(&TASK->kernel_box, call);
749
                ipc_answer(&TASK->kernel_box, call);
745
 
750
 
746
                ipl = interrupts_disable();
751
                ipl = interrupts_disable();
747
                spinlock_lock(&TASK->lock);
752
                spinlock_lock(&TASK->lock);
748
                spinlock_lock(&TASK->answerbox.lock);
753
                spinlock_lock(&TASK->answerbox.lock);
749
                if (list_empty(&TASK->answerbox.connected_phones)) {
754
                if (list_empty(&TASK->answerbox.connected_phones)) {
750
                    /* Last phone has been disconnected */
755
                    /* Last phone has been disconnected */
751
                    TASK->kb_thread = NULL;
756
                    TASK->kb_thread = NULL;
752
                    done = true;
757
                    done = true;
753
                    klog_printf("phone list is empty");
758
                    klog_printf("phone list is empty");
754
                }
759
                }
755
                spinlock_unlock(&TASK->answerbox.lock);
760
                spinlock_unlock(&TASK->answerbox.lock);
756
                spinlock_unlock(&TASK->lock);
761
                spinlock_unlock(&TASK->lock);
757
                interrupts_restore(ipl);
762
                interrupts_restore(ipl);
758
            }
763
            }
759
        }
764
        }
760
    }
765
    }
761
 
766
 
762
    klog_printf("kbox: finished");
767
    klog_printf("kbox: finished");
763
}
768
}
764
 
769
 
765
 
770
 
766
/**
771
/**
767
 * Connect phone to a task kernel-box specified by id.
772
 * Connect phone to a task kernel-box specified by id.
768
 *
773
 *
769
 * Note that this is not completely atomic. For optimisation reasons,
774
 * Note that this is not completely atomic. For optimisation reasons,
770
 * The task might start cleaning up kbox after the phone has been connected
775
 * The task might start cleaning up kbox after the phone has been connected
771
 * and before a kbox thread has been created. This must be taken into account
776
 * and before a kbox thread has been created. This must be taken into account
772
 * in the cleanup code.
777
 * in the cleanup code.
773
 *
778
 *
774
 * @return      Phone id on success, or negative error code.
779
 * @return      Phone id on success, or negative error code.
775
 */
780
 */
776
int ipc_connect_kbox(task_id_t taskid)
781
int ipc_connect_kbox(task_id_t taskid)
777
{
782
{
778
    int newphid;
783
    int newphid;
779
    task_t *ta;
784
    task_t *ta;
780
    thread_t *kb_thread;
785
    thread_t *kb_thread;
781
    ipl_t ipl;
786
    ipl_t ipl;
782
 
787
 
783
    ipl = interrupts_disable();
788
    ipl = interrupts_disable();
784
    spinlock_lock(&tasks_lock);
789
    spinlock_lock(&tasks_lock);
785
 
790
 
786
    ta = task_find_by_id(taskid);
791
    ta = task_find_by_id(taskid);
787
    if (ta == NULL) {
792
    if (ta == NULL) {
788
        spinlock_unlock(&tasks_lock);
793
        spinlock_unlock(&tasks_lock);
789
        interrupts_restore(ipl);
794
        interrupts_restore(ipl);
790
        return ENOENT;
795
        return ENOENT;
791
    }
796
    }
792
 
797
 
793
    atomic_inc(&ta->refcount);
798
    atomic_inc(&ta->refcount);
794
 
799
 
795
    spinlock_unlock(&tasks_lock);
800
    spinlock_unlock(&tasks_lock);
796
    interrupts_restore(ipl);
801
    interrupts_restore(ipl);
797
 
802
 
798
    mutex_lock(&ta->kb_cleanup_lock);
803
    mutex_lock(&ta->kb_cleanup_lock);
799
 
804
 
800
    if (atomic_predec(&ta->refcount) == 0) {
805
    if (atomic_predec(&ta->refcount) == 0) {
801
        mutex_unlock(&ta->kb_cleanup_lock);
806
        mutex_unlock(&ta->kb_cleanup_lock);
802
        task_destroy(ta);
807
        task_destroy(ta);
803
        return ENOENT;
808
        return ENOENT;
804
    }
809
    }
805
 
810
 
806
    if (ta->kb_finished != false) {
811
    if (ta->kb_finished != false) {
807
        mutex_unlock(&ta->kb_cleanup_lock);
812
        mutex_unlock(&ta->kb_cleanup_lock);
808
        return EINVAL;
813
        return EINVAL;
809
    }
814
    }
810
 
815
 
811
    newphid = phone_alloc();
816
    newphid = phone_alloc();
812
    if (newphid < 0) {
817
    if (newphid < 0) {
813
        mutex_unlock(&ta->kb_cleanup_lock);
818
        mutex_unlock(&ta->kb_cleanup_lock);
814
        return ELIMIT;
819
        return ELIMIT;
815
    }
820
    }
816
 
821
 
817
    /* Connect the newly allocated phone to the kbox */
822
    /* Connect the newly allocated phone to the kbox */
818
    ipc_phone_connect(&TASK->phones[newphid], &ta->kernel_box);
823
    ipc_phone_connect(&TASK->phones[newphid], &ta->kernel_box);
819
 
824
 
820
    if (ta->kb_thread != NULL) {
825
    if (ta->kb_thread != NULL) {
821
        mutex_unlock(&ta->kb_cleanup_lock);
826
        mutex_unlock(&ta->kb_cleanup_lock);
822
        return newphid;
827
        return newphid;
823
    }
828
    }
824
 
829
 
825
    /* Create a kbox thread */
830
    /* Create a kbox thread */
826
    kb_thread = thread_create(kbox_thread_proc, NULL, ta, 0, "kbox", false);
831
    kb_thread = thread_create(kbox_thread_proc, NULL, ta, 0, "kbox", false);
827
    if (!kb_thread) {
832
    if (!kb_thread) {
828
        mutex_unlock(&ta->kb_cleanup_lock);
833
        mutex_unlock(&ta->kb_cleanup_lock);
829
        return ENOMEM;
834
        return ENOMEM;
830
    }
835
    }
831
 
836
 
832
    ta->kb_thread = kb_thread;
837
    ta->kb_thread = kb_thread;
833
    thread_ready(kb_thread);
838
    thread_ready(kb_thread);
834
 
839
 
835
    mutex_unlock(&ta->kb_cleanup_lock);
840
    mutex_unlock(&ta->kb_cleanup_lock);
836
 
841
 
837
    return newphid;
842
    return newphid;
838
}
843
}
839
 
844
 
840
/** @}
845
/** @}
841
 */
846
 */
842
 
847