Rev 1223 | Rev 1260 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
| Rev 1223 | Rev 1258 | ||
|---|---|---|---|
| Line 41... | Line 41... | ||
| 41 | #include <memstr.h> |
41 | #include <memstr.h> |
| 42 | #include <debug.h> |
42 | #include <debug.h> |
| 43 | 43 | ||
| 44 | #include <print.h> |
44 | #include <print.h> |
| 45 | #include <proc/thread.h> |
45 | #include <proc/thread.h> |
| - | 46 | #include <arch/interrupt.h> |
|
| 46 | 47 | ||
| 47 | /* Open channel that is assigned automatically to new tasks */ |
48 | /* Open channel that is assigned automatically to new tasks */ |
| 48 | answerbox_t *ipc_phone_0 = NULL; |
49 | answerbox_t *ipc_phone_0 = NULL; |
| 49 | 50 | ||
| 50 | static slab_cache_t *ipc_call_slab; |
51 | static slab_cache_t *ipc_call_slab; |
| 51 | 52 | ||
| - | 53 | typedef struct { |
|
| - | 54 | SPINLOCK_DECLARE(lock); |
|
| - | 55 | answerbox_t *box; |
|
| - | 56 | } ipc_irq_t; |
|
| - | 57 | ||
| - | 58 | static ipc_irq_t *irq_conns = NULL; |
|
| - | 59 | static int irq_conns_size; |
|
| - | 60 | ||
| 52 | /* Initialize new call */ |
61 | /* Initialize new call */ |
| 53 | static void _ipc_call_init(call_t *call) |
62 | static void _ipc_call_init(call_t *call) |
| 54 | { |
63 | { |
| 55 | memsetb((__address)call, sizeof(*call), 0); |
64 | memsetb((__address)call, sizeof(*call), 0); |
| 56 | call->callerbox = &TASK->answerbox; |
65 | call->callerbox = &TASK->answerbox; |
| Line 59... | Line 68... | ||
| 59 | 68 | ||
| 60 | /** Allocate & initialize call structure |
69 | /** Allocate & initialize call structure |
| 61 | * |
70 | * |
| 62 | * The call is initialized, so that the reply will be directed |
71 | * The call is initialized, so that the reply will be directed |
| 63 | * to TASK->answerbox |
72 | * to TASK->answerbox |
| - | 73 | * |
|
| - | 74 | * @param flags Parameters for slab_alloc (ATOMIC, etc.) |
|
| 64 | */ |
75 | */ |
| 65 | call_t * ipc_call_alloc(void) |
76 | call_t * ipc_call_alloc(int flags) |
| 66 | { |
77 | { |
| 67 | call_t *call; |
78 | call_t *call; |
| 68 | 79 | ||
| 69 | call = slab_alloc(ipc_call_slab, 0); |
80 | call = slab_alloc(ipc_call_slab, flags); |
| 70 | _ipc_call_init(call); |
81 | _ipc_call_init(call); |
| 71 | 82 | ||
| 72 | return call; |
83 | return call; |
| 73 | } |
84 | } |
| 74 | 85 | ||
| Line 88... | Line 99... | ||
| 88 | /** Initialize answerbox structure |
99 | /** Initialize answerbox structure |
| 89 | */ |
100 | */ |
| 90 | void ipc_answerbox_init(answerbox_t *box) |
101 | void ipc_answerbox_init(answerbox_t *box) |
| 91 | { |
102 | { |
| 92 | spinlock_initialize(&box->lock, "ipc_box_lock"); |
103 | spinlock_initialize(&box->lock, "ipc_box_lock"); |
| - | 104 | spinlock_initialize(&box->irq_lock, "ipc_box_irqlock"); |
|
| 93 | waitq_initialize(&box->wq); |
105 | waitq_initialize(&box->wq); |
| 94 | list_initialize(&box->connected_phones); |
106 | list_initialize(&box->connected_phones); |
| 95 | list_initialize(&box->calls); |
107 | list_initialize(&box->calls); |
| 96 | list_initialize(&box->dispatched_calls); |
108 | list_initialize(&box->dispatched_calls); |
| 97 | list_initialize(&box->answers); |
109 | list_initialize(&box->answers); |
| - | 110 | list_initialize(&box->irq_notifs); |
|
| 98 | box->task = TASK; |
111 | box->task = TASK; |
| 99 | } |
112 | } |
| 100 | 113 | ||
| 101 | /** Connect phone to answerbox */ |
114 | /** Connect phone to answerbox */ |
| 102 | void ipc_phone_connect(phone_t *phone, answerbox_t *box) |
115 | void ipc_phone_connect(phone_t *phone, answerbox_t *box) |
| Line 259... | Line 272... | ||
| 259 | spinlock_lock(&box->lock); |
272 | spinlock_lock(&box->lock); |
| 260 | list_remove(&phone->list); |
273 | list_remove(&phone->list); |
| 261 | phone->callee = NULL; |
274 | phone->callee = NULL; |
| 262 | spinlock_unlock(&box->lock); |
275 | spinlock_unlock(&box->lock); |
| 263 | 276 | ||
| 264 | call = ipc_call_alloc(); |
277 | call = ipc_call_alloc(0); |
| 265 | IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP); |
278 | IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP); |
| 266 | call->flags |= IPC_CALL_DISCARD_ANSWER; |
279 | call->flags |= IPC_CALL_DISCARD_ANSWER; |
| 267 | _ipc_call(phone, box, call); |
280 | _ipc_call(phone, box, call); |
| 268 | 281 | ||
| 269 | phone->busy = IPC_BUSY_FREE; |
282 | phone->busy = IPC_BUSY_FREE; |
| Line 299... | Line 312... | ||
| 299 | * - to distinguish between call and answer, look at call->flags |
312 | * - to distinguish between call and answer, look at call->flags |
| 300 | */ |
313 | */ |
| 301 | call_t * ipc_wait_for_call(answerbox_t *box, int flags) |
314 | call_t * ipc_wait_for_call(answerbox_t *box, int flags) |
| 302 | { |
315 | { |
| 303 | call_t *request; |
316 | call_t *request; |
| - | 317 | ipl_t ipl; |
|
| 304 | 318 | ||
| 305 | restart: |
319 | restart: |
| 306 | if (flags & IPC_WAIT_NONBLOCKING) { |
320 | if (flags & IPC_WAIT_NONBLOCKING) { |
| 307 | if (waitq_sleep_timeout(&box->wq,0,1) == ESYNCH_WOULD_BLOCK) |
321 | if (waitq_sleep_timeout(&box->wq,0,1) == ESYNCH_WOULD_BLOCK) |
| 308 | return NULL; |
322 | return NULL; |
| 309 | } else |
323 | } else |
| 310 | waitq_sleep(&box->wq); |
324 | waitq_sleep(&box->wq); |
| 311 | 325 | ||
| 312 | spinlock_lock(&box->lock); |
326 | spinlock_lock(&box->lock); |
| - | 327 | if (!list_empty(&box->irq_notifs)) { |
|
| - | 328 | ipl = interrupts_disable(); |
|
| - | 329 | spinlock_lock(&box->irq_lock); |
|
| - | 330 | ||
| - | 331 | request = list_get_instance(box->answers.next, call_t, list); |
|
| - | 332 | list_remove(&request->list); |
|
| - | 333 | ||
| - | 334 | spinlock_unlock(&box->irq_lock); |
|
| - | 335 | interrupts_restore(ipl); |
|
| 313 | if (!list_empty(&box->answers)) { |
336 | } else if (!list_empty(&box->answers)) { |
| 314 | /* Handle asynchronous answers */ |
337 | /* Handle asynchronous answers */ |
| 315 | request = list_get_instance(box->answers.next, call_t, list); |
338 | request = list_get_instance(box->answers.next, call_t, list); |
| 316 | list_remove(&request->list); |
339 | list_remove(&request->list); |
| 317 | atomic_dec(&request->data.phone->active_calls); |
340 | atomic_dec(&request->data.phone->active_calls); |
| 318 | } else if (!list_empty(&box->calls)) { |
341 | } else if (!list_empty(&box->calls)) { |
| Line 331... | Line 354... | ||
| 331 | } |
354 | } |
| 332 | spinlock_unlock(&box->lock); |
355 | spinlock_unlock(&box->lock); |
| 333 | return request; |
356 | return request; |
| 334 | } |
357 | } |
| 335 | 358 | ||
| 336 | /** Initilize ipc subsystem */ |
- | |
| 337 | void ipc_init(void) |
- | |
| 338 | { |
- | |
| 339 | ipc_call_slab = slab_cache_create("ipc_call", |
- | |
| 340 | sizeof(call_t), |
- | |
| 341 | 0, |
- | |
| 342 | NULL, NULL, 0); |
- | |
| 343 | } |
- | |
| 344 | - | ||
| 345 | /** Answer all calls from list with EHANGUP msg */ |
359 | /** Answer all calls from list with EHANGUP msg */ |
| 346 | static void ipc_cleanup_call_list(link_t *lst) |
360 | static void ipc_cleanup_call_list(link_t *lst) |
| 347 | { |
361 | { |
| 348 | call_t *call; |
362 | call_t *call; |
| 349 | 363 | ||
| Line 354... | Line 368... | ||
| 354 | IPC_SET_RETVAL(call->data, EHANGUP); |
368 | IPC_SET_RETVAL(call->data, EHANGUP); |
| 355 | _ipc_answer_free_call(call); |
369 | _ipc_answer_free_call(call); |
| 356 | } |
370 | } |
| 357 | } |
371 | } |
| 358 | 372 | ||
| - | 373 | /** Disconnect all irq's notifications |
|
| - | 374 | * |
|
| - | 375 | * TODO: It may be better to do some linked list, so that |
|
| - | 376 | * we wouldn't need to go through whole array every cleanup |
|
| - | 377 | */ |
|
| - | 378 | static void ipc_irq_cleanup(answerbox_t *box) |
|
| - | 379 | { |
|
| - | 380 | int i; |
|
| - | 381 | ipl_t ipl; |
|
| - | 382 | ||
| - | 383 | for (i=0; i < irq_conns_size; i++) { |
|
| - | 384 | ipl = interrupts_disable(); |
|
| - | 385 | spinlock_lock(&irq_conns[i].lock); |
|
| - | 386 | if (irq_conns[i].box == box) |
|
| - | 387 | irq_conns[i].box = NULL; |
|
| - | 388 | spinlock_unlock(&irq_conns[i].lock); |
|
| - | 389 | interrupts_restore(ipl); |
|
| - | 390 | } |
|
| - | 391 | } |
|
| - | 392 | ||
| 359 | /** Cleans up all IPC communication of the given task |
393 | /** Cleans up all IPC communication of the given task |
| 360 | * |
394 | * |
| 361 | * |
395 | * |
| 362 | */ |
396 | */ |
| 363 | void ipc_cleanup(task_t *task) |
397 | void ipc_cleanup(task_t *task) |
| Line 368... | Line 402... | ||
| 368 | 402 | ||
| 369 | /* Disconnect all our phones ('ipc_phone_hangup') */ |
403 | /* Disconnect all our phones ('ipc_phone_hangup') */ |
| 370 | for (i=0;i < IPC_MAX_PHONES; i++) |
404 | for (i=0;i < IPC_MAX_PHONES; i++) |
| 371 | ipc_phone_hangup(&task->phones[i]); |
405 | ipc_phone_hangup(&task->phones[i]); |
| 372 | 406 | ||
| - | 407 | /* Disconnect all connected irqs */ |
|
| - | 408 | ipc_irq_cleanup(&task->answerbox); |
|
| - | 409 | ||
| 373 | /* Disconnect all phones connected to our answerbox */ |
410 | /* Disconnect all phones connected to our answerbox */ |
| 374 | restart_phones: |
411 | restart_phones: |
| 375 | spinlock_lock(&task->answerbox.lock); |
412 | spinlock_lock(&task->answerbox.lock); |
| 376 | while (!list_empty(&task->answerbox.connected_phones)) { |
413 | while (!list_empty(&task->answerbox.connected_phones)) { |
| 377 | phone = list_get_instance(task->answerbox.connected_phones.next, |
414 | phone = list_get_instance(task->answerbox.connected_phones.next, |
| Line 403... | Line 440... | ||
| 403 | 440 | ||
| 404 | atomic_dec(&task->active_calls); |
441 | atomic_dec(&task->active_calls); |
| 405 | ipc_call_free(call); |
442 | ipc_call_free(call); |
| 406 | } |
443 | } |
| 407 | } |
444 | } |
| - | 445 | ||
| - | 446 | /** Initialize table of interrupt handlers */ |
|
| - | 447 | static void ipc_irq_make_table(int irqcount) |
|
| - | 448 | { |
|
| - | 449 | int i; |
|
| - | 450 | ||
| - | 451 | irq_conns_size = irqcount; |
|
| - | 452 | irq_conns = malloc(irqcount * (sizeof(*irq_conns)), 0); |
|
| - | 453 | for (i=0; i < irqcount; i++) { |
|
| - | 454 | spinlock_initialize(&irq_conns[i].lock, "irq_ipc_lock"); |
|
| - | 455 | irq_conns[i].box = NULL; |
|
| - | 456 | } |
|
| - | 457 | } |
|
| - | 458 | ||
| - | 459 | void ipc_irq_unregister(answerbox_t *box, int irq) |
|
| - | 460 | { |
|
| - | 461 | ipl_t ipl; |
|
| - | 462 | ||
| - | 463 | ipl = interrupts_disable(); |
|
| - | 464 | spinlock_lock(&irq_conns[irq].lock); |
|
| - | 465 | if (irq_conns[irq].box == box) |
|
| - | 466 | irq_conns[irq].box = NULL; |
|
| - | 467 | ||
| - | 468 | spinlock_unlock(&irq_conns[irq].lock); |
|
| - | 469 | interrupts_restore(ipl); |
|
| - | 470 | } |
|
| - | 471 | ||
| - | 472 | /** Register an answerbox as a receiving end of interrupts notifications */ |
|
| - | 473 | int ipc_irq_register(answerbox_t *box, int irq) |
|
| - | 474 | { |
|
| - | 475 | ipl_t ipl; |
|
| - | 476 | ||
| - | 477 | ASSERT(irq_conns); |
|
| - | 478 | ||
| - | 479 | ipl = interrupts_disable(); |
|
| - | 480 | spinlock_lock(&irq_conns[irq].lock); |
|
| - | 481 | ||
| - | 482 | if (irq_conns[irq].box) { |
|
| - | 483 | spinlock_unlock(&irq_conns[irq].lock); |
|
| - | 484 | interrupts_restore(ipl); |
|
| - | 485 | return EEXISTS; |
|
| - | 486 | } |
|
| - | 487 | irq_conns[irq].box = box; |
|
| - | 488 | spinlock_unlock(&irq_conns[irq].lock); |
|
| - | 489 | interrupts_restore(ipl); |
|
| - | 490 | ||
| - | 491 | return 0; |
|
| - | 492 | } |
|
| - | 493 | ||
| - | 494 | /** Notify process that an irq had happend |
|
| - | 495 | * |
|
| - | 496 | * We expect interrupts to be disabled |
|
| - | 497 | */ |
|
| - | 498 | void ipc_irq_send_notif(int irq) |
|
| - | 499 | { |
|
| - | 500 | call_t *call; |
|
| - | 501 | ||
| - | 502 | ASSERT(irq_conns); |
|
| - | 503 | spinlock_lock(&irq_conns[irq].lock); |
|
| - | 504 | ||
| - | 505 | if (irq_conns[irq].box) { |
|
| - | 506 | call = ipc_call_alloc(FRAME_ATOMIC); |
|
| - | 507 | call->flags |= IPC_CALL_NOTIF; |
|
| - | 508 | IPC_SET_METHOD(call->data, IPC_M_INTERRUPT); |
|
| - | 509 | IPC_SET_ARG1(call->data, irq); |
|
| - | 510 | ||
| - | 511 | spinlock_lock(&irq_conns[irq].box->irq_lock); |
|
| - | 512 | list_append(&call->list, &irq_conns[irq].box->irq_notifs); |
|
| - | 513 | spinlock_unlock(&irq_conns[irq].box->irq_lock); |
|
| - | 514 | ||
| - | 515 | waitq_wakeup(&irq_conns[irq].box->wq, 0); |
|
| - | 516 | } |
|
| - | 517 | ||
| - | 518 | spinlock_unlock(&irq_conns[irq].lock); |
|
| - | 519 | } |
|
| - | 520 | ||
| - | 521 | /** Initilize ipc subsystem */ |
|
| - | 522 | void ipc_init(void) |
|
| - | 523 | { |
|
| - | 524 | ipc_call_slab = slab_cache_create("ipc_call", |
|
| - | 525 | sizeof(call_t), |
|
| - | 526 | 0, |
|
| - | 527 | NULL, NULL, 0); |
|
| - | 528 | ipc_irq_make_table(IRQ_COUNT); |
|
| - | 529 | } |
|
| - | 530 | ||