00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00035
00036
00037
00038
00039
00040 #include <synch/spinlock.h>
00041 #include <synch/waitq.h>
00042 #include <synch/synch.h>
00043 #include <ipc/ipc.h>
00044 #include <errno.h>
00045 #include <mm/slab.h>
00046 #include <arch.h>
00047 #include <proc/task.h>
00048 #include <memstr.h>
00049 #include <debug.h>
00050
00051 #include <print.h>
00052 #include <proc/thread.h>
00053 #include <arch/interrupt.h>
00054 #include <ipc/irq.h>
00055
00056
00057 answerbox_t *ipc_phone_0 = NULL;
00058
00059 static slab_cache_t *ipc_call_slab;
00060
00061
00062 static void _ipc_call_init(call_t *call)
00063 {
00064 memsetb((__address)call, sizeof(*call), 0);
00065 call->callerbox = &TASK->answerbox;
00066 call->sender = TASK;
00067 }
00068
00076 call_t * ipc_call_alloc(int flags)
00077 {
00078 call_t *call;
00079
00080 call = slab_alloc(ipc_call_slab, flags);
00081 _ipc_call_init(call);
00082
00083 return call;
00084 }
00085
00087 void ipc_call_static_init(call_t *call)
00088 {
00089 _ipc_call_init(call);
00090 call->flags |= IPC_CALL_STATIC_ALLOC;
00091 }
00092
00094 void ipc_call_free(call_t *call)
00095 {
00096 slab_free(ipc_call_slab, call);
00097 }
00098
00101 void ipc_answerbox_init(answerbox_t *box)
00102 {
00103 spinlock_initialize(&box->lock, "ipc_box_lock");
00104 spinlock_initialize(&box->irq_lock, "ipc_box_irqlock");
00105 waitq_initialize(&box->wq);
00106 list_initialize(&box->connected_phones);
00107 list_initialize(&box->calls);
00108 list_initialize(&box->dispatched_calls);
00109 list_initialize(&box->answers);
00110 list_initialize(&box->irq_notifs);
00111 box->task = TASK;
00112 }
00113
00115 void ipc_phone_connect(phone_t *phone, answerbox_t *box)
00116 {
00117 spinlock_lock(&phone->lock);
00118
00119 phone->state = IPC_PHONE_CONNECTED;
00120 phone->callee = box;
00121
00122 spinlock_lock(&box->lock);
00123 list_append(&phone->link, &box->connected_phones);
00124 spinlock_unlock(&box->lock);
00125
00126 spinlock_unlock(&phone->lock);
00127 }
00128
00131 void ipc_phone_init(phone_t *phone)
00132 {
00133 spinlock_initialize(&phone->lock, "phone_lock");
00134 phone->callee = NULL;
00135 phone->state = IPC_PHONE_FREE;
00136 atomic_set(&phone->active_calls, 0);
00137 }
00138
00140 void ipc_call_sync(phone_t *phone, call_t *request)
00141 {
00142 answerbox_t sync_box;
00143
00144 ipc_answerbox_init(&sync_box);
00145
00146
00147 request->callerbox = &sync_box;
00148
00149 ipc_call(phone, request);
00150 ipc_wait_for_call(&sync_box, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE);
00151 }
00152
00156 static void _ipc_answer_free_call(call_t *call)
00157 {
00158 answerbox_t *callerbox = call->callerbox;
00159
00160 call->flags |= IPC_CALL_ANSWERED;
00161
00162 spinlock_lock(&callerbox->lock);
00163 list_append(&call->link, &callerbox->answers);
00164 spinlock_unlock(&callerbox->lock);
00165 waitq_wakeup(&callerbox->wq, 0);
00166 }
00167
00173 void ipc_answer(answerbox_t *box, call_t *call)
00174 {
00175
00176 spinlock_lock(&box->lock);
00177 list_remove(&call->link);
00178 spinlock_unlock(&box->lock);
00179
00180 _ipc_answer_free_call(call);
00181 }
00182
00188 void ipc_backsend_err(phone_t *phone, call_t *call, __native err)
00189 {
00190 call->data.phone = phone;
00191 atomic_inc(&phone->active_calls);
00192 IPC_SET_RETVAL(call->data, err);
00193 _ipc_answer_free_call(call);
00194 }
00195
00196
00197 static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
00198 {
00199 if (! (call->flags & IPC_CALL_FORWARDED)) {
00200 atomic_inc(&phone->active_calls);
00201 call->data.phone = phone;
00202 }
00203
00204 spinlock_lock(&box->lock);
00205 list_append(&call->link, &box->calls);
00206 spinlock_unlock(&box->lock);
00207 waitq_wakeup(&box->wq, 0);
00208 }
00209
00215 int ipc_call(phone_t *phone, call_t *call)
00216 {
00217 answerbox_t *box;
00218
00219 spinlock_lock(&phone->lock);
00220 if (phone->state != IPC_PHONE_CONNECTED) {
00221 spinlock_unlock(&phone->lock);
00222 if (call->flags & IPC_CALL_FORWARDED) {
00223 IPC_SET_RETVAL(call->data, EFORWARD);
00224 _ipc_answer_free_call(call);
00225 } else {
00226 if (phone->state == IPC_PHONE_HUNGUP)
00227 ipc_backsend_err(phone, call, EHANGUP);
00228 else
00229 ipc_backsend_err(phone, call, ENOENT);
00230 }
00231 return ENOENT;
00232 }
00233 box = phone->callee;
00234 _ipc_call(phone, box, call);
00235
00236 spinlock_unlock(&phone->lock);
00237 return 0;
00238 }
00239
00249 int ipc_phone_hangup(phone_t *phone)
00250 {
00251 answerbox_t *box;
00252 call_t *call;
00253
00254 spinlock_lock(&phone->lock);
00255 if (phone->state == IPC_PHONE_FREE || phone->state ==IPC_PHONE_HUNGUP \
00256 || phone->state == IPC_PHONE_CONNECTING) {
00257 spinlock_unlock(&phone->lock);
00258 return -1;
00259 }
00260 box = phone->callee;
00261 if (phone->state != IPC_PHONE_SLAMMED) {
00262
00263 spinlock_lock(&box->lock);
00264 list_remove(&phone->link);
00265 spinlock_unlock(&box->lock);
00266
00267 if (phone->state != IPC_PHONE_SLAMMED) {
00268 call = ipc_call_alloc(0);
00269 IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
00270 call->flags |= IPC_CALL_DISCARD_ANSWER;
00271 _ipc_call(phone, box, call);
00272 }
00273 }
00274
00275 phone->state = IPC_PHONE_HUNGUP;
00276 spinlock_unlock(&phone->lock);
00277
00278 return 0;
00279 }
00280
00291 int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox)
00292 {
00293 spinlock_lock(&oldbox->lock);
00294 list_remove(&call->link);
00295 spinlock_unlock(&oldbox->lock);
00296
00297 return ipc_call(newphone, call);
00298 }
00299
00300
00311 call_t * ipc_wait_for_call(answerbox_t *box, __u32 usec, int flags)
00312 {
00313 call_t *request;
00314 ipl_t ipl;
00315 int rc;
00316
00317 restart:
00318 rc = waitq_sleep_timeout(&box->wq, usec, flags);
00319 if (SYNCH_FAILED(rc))
00320 return NULL;
00321
00322 spinlock_lock(&box->lock);
00323 if (!list_empty(&box->irq_notifs)) {
00324 ipl = interrupts_disable();
00325 spinlock_lock(&box->irq_lock);
00326
00327 request = list_get_instance(box->irq_notifs.next, call_t, link);
00328 list_remove(&request->link);
00329
00330 spinlock_unlock(&box->irq_lock);
00331 interrupts_restore(ipl);
00332 } else if (!list_empty(&box->answers)) {
00333
00334 request = list_get_instance(box->answers.next, call_t, link);
00335 list_remove(&request->link);
00336 atomic_dec(&request->data.phone->active_calls);
00337 } else if (!list_empty(&box->calls)) {
00338
00339 request = list_get_instance(box->calls.next, call_t, link);
00340 list_remove(&request->link);
00341
00342 list_append(&request->link, &box->dispatched_calls);
00343 } else {
00344
00345 spinlock_unlock(&box->lock);
00346 goto restart;
00347 }
00348 spinlock_unlock(&box->lock);
00349 return request;
00350 }
00351
00353 static void ipc_cleanup_call_list(link_t *lst)
00354 {
00355 call_t *call;
00356
00357 while (!list_empty(lst)) {
00358 call = list_get_instance(lst->next, call_t, link);
00359 list_remove(&call->link);
00360
00361 IPC_SET_RETVAL(call->data, EHANGUP);
00362 _ipc_answer_free_call(call);
00363 }
00364 }
00365
00371 void ipc_cleanup(void)
00372 {
00373 int i;
00374 call_t *call;
00375 phone_t *phone;
00376
00377
00378 for (i=0;i < IPC_MAX_PHONES; i++)
00379 ipc_phone_hangup(&TASK->phones[i]);
00380
00381
00382 ipc_irq_cleanup(&TASK->answerbox);
00383
00384
00385 restart_phones:
00386 spinlock_lock(&TASK->answerbox.lock);
00387 while (!list_empty(&TASK->answerbox.connected_phones)) {
00388 phone = list_get_instance(TASK->answerbox.connected_phones.next,
00389 phone_t, link);
00390 if (! spinlock_trylock(&phone->lock)) {
00391 spinlock_unlock(&TASK->answerbox.lock);
00392 goto restart_phones;
00393 }
00394
00395
00396 ASSERT(phone->state == IPC_PHONE_CONNECTED);
00397 phone->state = IPC_PHONE_SLAMMED;
00398 list_remove(&phone->link);
00399
00400 spinlock_unlock(&phone->lock);
00401 }
00402
00403
00404 ipc_cleanup_call_list(&TASK->answerbox.dispatched_calls);
00405 ipc_cleanup_call_list(&TASK->answerbox.calls);
00406 spinlock_unlock(&TASK->answerbox.lock);
00407
00408
00409 while (1) {
00410
00411
00412
00413 for (i=0;i < IPC_MAX_PHONES; i++) {
00414 if (TASK->phones[i].state == IPC_PHONE_HUNGUP && \
00415 atomic_get(&TASK->phones[i].active_calls) == 0)
00416 TASK->phones[i].state = IPC_PHONE_FREE;
00417
00418
00419
00420 if (TASK->phones[i].state == IPC_PHONE_CONNECTED)
00421 ipc_phone_hangup(&TASK->phones[i]);
00422
00423
00424
00425
00426 if (TASK->phones[i].state != IPC_PHONE_FREE)
00427 break;
00428 }
00429
00430 if (i == IPC_MAX_PHONES)
00431 break;
00432
00433 call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE);
00434 ASSERT((call->flags & IPC_CALL_ANSWERED) || (call->flags & IPC_CALL_NOTIF));
00435 ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
00436
00437 atomic_dec(&TASK->active_calls);
00438 ipc_call_free(call);
00439 }
00440 }
00441
00442
00444 void ipc_init(void)
00445 {
00446 ipc_call_slab = slab_cache_create("ipc_call",
00447 sizeof(call_t),
00448 0,
00449 NULL, NULL, 0);
00450 ipc_irq_make_table(IRQ_COUNT);
00451 }
00452
00453
00455 void ipc_print_task(task_id_t taskid)
00456 {
00457 task_t *task;
00458 int i;
00459 call_t *call;
00460 link_t *tmp;
00461
00462 spinlock_lock(&tasks_lock);
00463 task = task_find_by_id(taskid);
00464 if (task)
00465 spinlock_lock(&task->lock);
00466 spinlock_unlock(&tasks_lock);
00467 if (!task)
00468 return;
00469
00470
00471 printf("PHONE:\n");
00472 for (i=0; i < IPC_MAX_PHONES;i++) {
00473 spinlock_lock(&task->phones[i].lock);
00474 if (task->phones[i].state != IPC_PHONE_FREE) {
00475 printf("%d: ",i);
00476 switch (task->phones[i].state) {
00477 case IPC_PHONE_CONNECTING:
00478 printf("connecting ");
00479 break;
00480 case IPC_PHONE_CONNECTED:
00481 printf("connected to: %p ",
00482 task->phones[i].callee);
00483 break;
00484 case IPC_PHONE_SLAMMED:
00485 printf("slammed by: %p ",
00486 task->phones[i].callee);
00487 break;
00488 case IPC_PHONE_HUNGUP:
00489 printf("hung up - was: %p ",
00490 task->phones[i].callee);
00491 break;
00492 default:
00493 break;
00494 }
00495 printf("active: %d\n", atomic_get(&task->phones[i].active_calls));
00496 }
00497 spinlock_unlock(&task->phones[i].lock);
00498 }
00499
00500
00501
00502 spinlock_lock(&task->answerbox.lock);
00503 printf("ABOX - CALLS:\n");
00504 for (tmp=task->answerbox.calls.next; tmp != &task->answerbox.calls;tmp = tmp->next) {
00505 call = list_get_instance(tmp, call_t, link);
00506 printf("Callid: %p Srctask:%lld M:%d A1:%d A2:%d A3:%d Flags:%x\n",call,
00507 call->sender->taskid, IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
00508 IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data), call->flags);
00509 }
00510
00511 printf("ABOX - DISPATCHED CALLS:\n");
00512 for (tmp=task->answerbox.dispatched_calls.next;
00513 tmp != &task->answerbox.dispatched_calls;
00514 tmp = tmp->next) {
00515 call = list_get_instance(tmp, call_t, link);
00516 printf("Callid: %p Srctask:%lld M:%d A1:%d A2:%d A3:%d Flags:%x\n",call,
00517 call->sender->taskid, IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
00518 IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data), call->flags);
00519 }
00520
00521 printf("ABOX - ANSWERS:\n");
00522 for (tmp=task->answerbox.answers.next; tmp != &task->answerbox.answers; tmp = tmp->next) {
00523 call = list_get_instance(tmp, call_t, link);
00524 printf("Callid:%p M:%d A1:%d A2:%d A3:%d Flags:%x\n",call,
00525 IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
00526 IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data), call->flags);
00527 }
00528
00529 spinlock_unlock(&task->answerbox.lock);
00530 spinlock_unlock(&task->lock);
00531 }
00532