Subversion Repositories HelenOS

Rev

Rev 1050 | Rev 1084 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (C) 2006 Ondrej Palkovsky
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * - Redistributions of source code must retain the above copyright
  10.  *   notice, this list of conditions and the following disclaimer.
  11.  * - Redistributions in binary form must reproduce the above copyright
  12.  *   notice, this list of conditions and the following disclaimer in the
  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
  15.  *   derived from this software without specific prior written permission.
  16.  *
  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
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  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
  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
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28.  
  29. /* Lock ordering
  30.  *
  31.  * First the answerbox, then the phone
  32.  */
  33.  
  34. #include <synch/spinlock.h>
  35. #include <synch/waitq.h>
  36. #include <ipc/ipc.h>
  37. #include <errno.h>
  38. #include <mm/slab.h>
  39. #include <arch.h>
  40. #include <proc/task.h>
  41. #include <memstr.h>
  42. #include <debug.h>
  43.  
  44. #include <print.h>
  45. #include <proc/thread.h>
  46.  
  47. /* Open channel that is assigned automatically to new tasks */
  48. answerbox_t *ipc_phone_0 = NULL;
  49.  
  50. static slab_cache_t *ipc_call_slab;
  51.  
  52. /** Allocate & initialize call structure
  53.  *
  54.  * The call is initialized, so that the reply will be directed
  55.  * to TASK->answerbox
  56.  */
  57. call_t * ipc_call_alloc(void)
  58. {
  59.     call_t *call;
  60.  
  61.     call = slab_alloc(ipc_call_slab, 0);
  62.     memsetb((__address)call, sizeof(*call), 0);
  63.     call->callerbox = &TASK->answerbox;
  64.     call->sender = TASK;
  65.  
  66.     return call;
  67. }
  68.  
  69. /** Initialize allocated call */
  70. void ipc_call_init(call_t *call)
  71. {
  72.     call->callerbox = &TASK->answerbox;
  73.     call->flags = IPC_CALL_STATIC_ALLOC;
  74.     call->sender = TASK;
  75. }
  76.  
  77. /** Deallocate call stracuture */
  78. void ipc_call_free(call_t *call)
  79. {
  80.     slab_free(ipc_call_slab, call);
  81. }
  82.  
  83. /** Initialize answerbox structure
  84.  */
  85. void ipc_answerbox_init(answerbox_t *box)
  86. {
  87.     spinlock_initialize(&box->lock, "ipc_box_lock");
  88.     waitq_initialize(&box->wq);
  89.     list_initialize(&box->connected_phones);
  90.     list_initialize(&box->calls);
  91.     list_initialize(&box->dispatched_calls);
  92.     list_initialize(&box->answers);
  93.     box->task = TASK;
  94. }
  95.  
  96. /** Connect phone to answerbox */
  97. void ipc_phone_connect(phone_t *phone, answerbox_t *box)
  98. {
  99.     ASSERT(!phone->callee);
  100.     phone->busy = 1;
  101.     phone->callee = box;
  102.  
  103.     spinlock_lock(&box->lock);
  104.     list_append(&phone->list, &box->connected_phones);
  105.     spinlock_unlock(&box->lock);
  106. }
  107.  
  108. /** Initialize phone structure and connect phone to naswerbox
  109.  */
  110. void ipc_phone_init(phone_t *phone)
  111. {
  112.     spinlock_initialize(&phone->lock, "phone_lock");
  113.     phone->callee = NULL;
  114.     phone->busy = 0;
  115. }
  116.  
  117. /** Disconnect phone from answerbox */
  118. void ipc_phone_destroy(phone_t *phone)
  119. {
  120.     answerbox_t *box = phone->callee;
  121.    
  122.     ASSERT(box);
  123.  
  124.     spinlock_lock(&box->lock);
  125.     list_remove(&phone->list);
  126.     spinlock_unlock(&box->lock);
  127. }
  128.  
  129. /** Helper function to facilitate synchronous calls */
  130. void ipc_call_sync(phone_t *phone, call_t *request)
  131. {
  132.     answerbox_t sync_box;
  133.  
  134.     ipc_answerbox_init(&sync_box);
  135.  
  136.     /* We will receive data on special box */
  137.     request->callerbox = &sync_box;
  138.  
  139.     ipc_call(phone, request);
  140.     ipc_wait_for_call(&sync_box, 0);
  141. }
  142.  
  143. /** Send a asynchronous request using phone to answerbox
  144.  *
  145.  * @param phone Phone connected to answerbox
  146.  * @param request Request to be sent
  147.  */
  148. void ipc_call(phone_t *phone, call_t *call)
  149. {
  150.     answerbox_t *box = phone->callee;
  151.  
  152.     ASSERT(box);
  153.  
  154.     spinlock_lock(&box->lock);
  155.     list_append(&call->list, &box->calls);
  156.     spinlock_unlock(&box->lock);
  157.     waitq_wakeup(&box->wq, 0);
  158. }
  159.  
  160. /** Forwards call from one answerbox to a new one
  161.  *
  162.  * @param request Request to be forwarded
  163.  * @param newbox Target answerbox
  164.  * @param oldbox Old answerbox
  165.  */
  166. void ipc_forward(call_t *call, answerbox_t *newbox, answerbox_t *oldbox)
  167. {
  168.     spinlock_lock(&oldbox->lock);
  169.     list_remove(&call->list);
  170.     spinlock_unlock(&oldbox->lock);
  171.  
  172.     spinlock_lock(&newbox->lock);
  173.     list_append(&call->list, &newbox->calls);
  174.     spinlock_lock(&newbox->lock);
  175.     waitq_wakeup(&newbox->wq, 0);
  176. }
  177.  
  178. /** Answer message back to phone
  179.  *
  180.  * @param box Answerbox that is answering the message
  181.  * @param request Modified request that is being sent back
  182.  */
  183. void ipc_answer(answerbox_t *box, call_t *request)
  184. {
  185.     answerbox_t *callerbox = request->callerbox;
  186.  
  187.     request->flags &= ~IPC_CALL_DISPATCHED;
  188.     request->flags |= IPC_CALL_ANSWERED;
  189.  
  190.     spinlock_lock(&box->lock);
  191.     list_remove(&request->list);
  192.     spinlock_unlock(&box->lock);
  193.  
  194.     spinlock_lock(&callerbox->lock);
  195.     list_append(&request->list, &callerbox->answers);
  196.     spinlock_unlock(&callerbox->lock);
  197.     waitq_wakeup(&callerbox->wq, 0);
  198. }
  199.  
  200. /** Wait for phone call
  201.  *
  202.  * @return Recived message address
  203.  * - to distinguish between call and answer, look at call->flags
  204.  */
  205. call_t * ipc_wait_for_call(answerbox_t *box, int flags)
  206. {
  207.     call_t *request;
  208.  
  209.     spinlock_lock(&box->lock);
  210.     while (1) {
  211.         if (!list_empty(&box->answers)) {
  212.             /* Handle asynchronous answers */
  213.             request = list_get_instance(box->answers.next, call_t, list);
  214.             list_remove(&request->list);
  215.         } else if (!list_empty(&box->calls)) {
  216.             /* Handle requests */
  217.             request = list_get_instance(box->calls.next, call_t, list);
  218.             list_remove(&request->list);
  219.             /* Append request to dispatch queue */
  220.             list_append(&request->list, &box->dispatched_calls);
  221.             request->flags |= IPC_CALL_DISPATCHED;
  222.         } else {
  223.             if (!(flags & IPC_WAIT_NONBLOCKING)) {
  224.                 /* Wait for event to appear */
  225.                 spinlock_unlock(&box->lock);
  226.                 waitq_sleep(&box->wq);
  227.                 spinlock_lock(&box->lock);
  228.                 continue;
  229.             }
  230.             request = NULL;
  231.         }
  232.         break;
  233.     }
  234.     spinlock_unlock(&box->lock);
  235.     return request;
  236. }
  237.  
  238. /** Initilize ipc subsystem */
  239. void ipc_init(void)
  240. {
  241.     ipc_call_slab = slab_cache_create("ipc_call",
  242.                       sizeof(call_t),
  243.                       0,
  244.                       NULL, NULL, 0);
  245. }
  246.  
  247. /** Cleans up all IPC communication of the given task
  248.  *
  249.  *
  250.  */
  251. void ipc_cleanup(task_t *task)
  252. {
  253.     /* Cancel all calls in my dispatch queue */
  254.    
  255. }
  256.