Subversion Repositories HelenOS

Rev

Rev 1787 | Rev 1923 | 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. /** @addtogroup genericipc
  30.  * @{
  31.  */
  32. /**
  33.  * @file
  34.  * @brief IRQ notification framework.
  35.  *
  36.  * This framework allows applications to register to receive a notification
  37.  * when interrupt is detected. The application may provide a simple 'top-half'
  38.  * handler as part of its registration, which can perform simple operations
  39.  * (read/write port/memory, add information to notification ipc message).
  40.  *
  41.  * The structure of a notification message is as follows:
  42.  * - METHOD: interrupt number
  43.  * - ARG1: payload modified by a 'top-half' handler
  44.  * - ARG2: payload
  45.  * - ARG3: payload
  46.  * - in_phone_hash: interrupt counter (may be needed to assure correct order
  47.  *         in multithreaded drivers)
  48.  */
  49.  
  50. #include <arch.h>
  51. #include <mm/slab.h>
  52. #include <errno.h>
  53. #include <ipc/ipc.h>
  54. #include <ipc/irq.h>
  55. #include <atomic.h>
  56. #include <syscall/copy.h>
  57. #include <console/console.h>
  58. #include <print.h>
  59.  
  60. typedef struct {
  61.     SPINLOCK_DECLARE(lock);
  62.     answerbox_t *box;
  63.     irq_code_t *code;
  64.     atomic_t counter;
  65. } ipc_irq_t;
  66.  
  67.  
  68. static ipc_irq_t *irq_conns = NULL;
  69. static int irq_conns_size;
  70.  
  71.  
  72. /* Execute code associated with IRQ notification */
  73. static void code_execute(call_t *call, irq_code_t *code)
  74. {
  75.     int i;
  76.     unative_t dstval = 0;
  77.    
  78.     if (!code)
  79.         return;
  80.    
  81.     for (i=0; i < code->cmdcount;i++) {
  82.         switch (code->cmds[i].cmd) {
  83.         case CMD_MEM_READ_1:
  84.             dstval = *((uint8_t *)code->cmds[i].addr);
  85.             break;
  86.         case CMD_MEM_READ_2:
  87.             dstval = *((uint16_t *)code->cmds[i].addr);
  88.             break;
  89.         case CMD_MEM_READ_4:
  90.             dstval = *((uint32_t *)code->cmds[i].addr);
  91.             break;
  92.         case CMD_MEM_READ_8:
  93.             dstval = *((uint64_t *)code->cmds[i].addr);
  94.             break;
  95.         case CMD_MEM_WRITE_1:
  96.             *((uint8_t *)code->cmds[i].addr) = code->cmds[i].value;
  97.             break;
  98.         case CMD_MEM_WRITE_2:
  99.             *((uint16_t *)code->cmds[i].addr) = code->cmds[i].value;
  100.             break;
  101.         case CMD_MEM_WRITE_4:
  102.             *((uint32_t *)code->cmds[i].addr) = code->cmds[i].value;
  103.             break;
  104.         case CMD_MEM_WRITE_8:
  105.             *((uint64_t *)code->cmds[i].addr) = code->cmds[i].value;
  106.             break;
  107. #if defined(ia32) || defined(amd64)
  108.         case CMD_PORT_READ_1:
  109.             dstval = inb((long)code->cmds[i].addr);
  110.             break;
  111.         case CMD_PORT_WRITE_1:
  112.             outb((long)code->cmds[i].addr, code->cmds[i].value);
  113.             break;
  114. #endif
  115. #if defined(ia64)
  116.         case CMD_IA64_GETCHAR:
  117.             dstval = _getc(&ski_uconsole);
  118.             break;
  119. #endif
  120. #if defined(ppc32)
  121.         case CMD_PPC32_GETCHAR:
  122.             dstval = cuda_get_scancode();
  123.             break;
  124. #endif
  125.         default:
  126.             break;
  127.         }
  128.         if (code->cmds[i].dstarg && code->cmds[i].dstarg < 4) {
  129.             call->data.args[code->cmds[i].dstarg] = dstval;
  130.         }
  131.     }
  132. }
  133.  
  134. static void code_free(irq_code_t *code)
  135. {
  136.     if (code) {
  137.         free(code->cmds);
  138.         free(code);
  139.     }
  140. }
  141.  
  142. static irq_code_t * code_from_uspace(irq_code_t *ucode)
  143. {
  144.     irq_code_t *code;
  145.     irq_cmd_t *ucmds;
  146.     int rc;
  147.  
  148.     code = malloc(sizeof(*code), 0);
  149.     rc = copy_from_uspace(code, ucode, sizeof(*code));
  150.     if (rc != 0) {
  151.         free(code);
  152.         return NULL;
  153.     }
  154.    
  155.     if (code->cmdcount > IRQ_MAX_PROG_SIZE) {
  156.         free(code);
  157.         return NULL;
  158.     }
  159.     ucmds = code->cmds;
  160.     code->cmds = malloc(sizeof(code->cmds[0]) * (code->cmdcount), 0);
  161.     rc = copy_from_uspace(code->cmds, ucmds, sizeof(code->cmds[0]) * (code->cmdcount));
  162.     if (rc != 0) {
  163.         free(code->cmds);
  164.         free(code);
  165.         return NULL;
  166.     }
  167.  
  168.     return code;
  169. }
  170.  
  171. /** Unregister task from irq */
  172. void ipc_irq_unregister(answerbox_t *box, int irq)
  173. {
  174.     ipl_t ipl;
  175.     int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
  176.  
  177.     ipl = interrupts_disable();
  178.     spinlock_lock(&irq_conns[mq].lock);
  179.     if (irq_conns[mq].box == box) {
  180.         irq_conns[mq].box = NULL;
  181.         code_free(irq_conns[mq].code);
  182.         irq_conns[mq].code = NULL;
  183.     }
  184.  
  185.     spinlock_unlock(&irq_conns[mq].lock);
  186.     interrupts_restore(ipl);
  187. }
  188.  
  189. /** Register an answerbox as a receiving end of interrupts notifications */
  190. int ipc_irq_register(answerbox_t *box, int irq, irq_code_t *ucode)
  191. {
  192.     ipl_t ipl;
  193.     irq_code_t *code;
  194.     int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
  195.  
  196.     ASSERT(irq_conns);
  197.  
  198.     if (ucode) {
  199.         code = code_from_uspace(ucode);
  200.         if (!code)
  201.             return EBADMEM;
  202.     } else
  203.         code = NULL;
  204.  
  205.     ipl = interrupts_disable();
  206.     spinlock_lock(&irq_conns[mq].lock);
  207.  
  208.     if (irq_conns[mq].box) {
  209.         spinlock_unlock(&irq_conns[mq].lock);
  210.         interrupts_restore(ipl);
  211.         code_free(code);
  212.         return EEXISTS;
  213.     }
  214.     irq_conns[mq].box = box;
  215.     irq_conns[mq].code = code;
  216.     atomic_set(&irq_conns[mq].counter, 0);
  217.     spinlock_unlock(&irq_conns[mq].lock);
  218.     interrupts_restore(ipl);
  219.  
  220.     return 0;
  221. }
  222.  
  223. /** Add call to proper answerbox queue
  224.  *
  225.  * Assume irq_conns[mq].lock is locked */
  226. static void send_call(int mq, call_t *call)
  227. {
  228.     spinlock_lock(&irq_conns[mq].box->irq_lock);
  229.     list_append(&call->link, &irq_conns[mq].box->irq_notifs);
  230.     spinlock_unlock(&irq_conns[mq].box->irq_lock);
  231.        
  232.     waitq_wakeup(&irq_conns[mq].box->wq, 0);
  233. }
  234.  
  235. /** Send notification message
  236.  *
  237.  */
  238. void ipc_irq_send_msg(int irq, unative_t a1, unative_t a2, unative_t a3)
  239. {
  240.     call_t *call;
  241.     int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
  242.  
  243.     spinlock_lock(&irq_conns[mq].lock);
  244.  
  245.     if (irq_conns[mq].box) {
  246.         call = ipc_call_alloc(FRAME_ATOMIC);
  247.         if (!call) {
  248.             spinlock_unlock(&irq_conns[mq].lock);
  249.             return;
  250.         }
  251.         call->flags |= IPC_CALL_NOTIF;
  252.         IPC_SET_METHOD(call->data, irq);
  253.         IPC_SET_ARG1(call->data, a1);
  254.         IPC_SET_ARG2(call->data, a2);
  255.         IPC_SET_ARG3(call->data, a3);
  256.         /* Put a counter to the message */
  257.         call->private = atomic_preinc(&irq_conns[mq].counter);
  258.        
  259.         send_call(mq, call);
  260.     }
  261.     spinlock_unlock(&irq_conns[mq].lock);
  262. }
  263.  
  264. /** Notify task that an irq had occurred.
  265.  *
  266.  * We expect interrupts to be disabled
  267.  */
  268. void ipc_irq_send_notif(int irq)
  269. {
  270.     call_t *call;
  271.     int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
  272.  
  273.     ASSERT(irq_conns);
  274.     spinlock_lock(&irq_conns[mq].lock);
  275.  
  276.     if (irq_conns[mq].box) {
  277.         call = ipc_call_alloc(FRAME_ATOMIC);
  278.         if (!call) {
  279.             spinlock_unlock(&irq_conns[mq].lock);
  280.             return;
  281.         }
  282.         call->flags |= IPC_CALL_NOTIF;
  283.         /* Put a counter to the message */
  284.         call->private = atomic_preinc(&irq_conns[mq].counter);
  285.         /* Set up args */
  286.         IPC_SET_METHOD(call->data, irq);
  287.  
  288.         /* Execute code to handle irq */
  289.         code_execute(call, irq_conns[mq].code);
  290.        
  291.         send_call(mq, call);
  292.     }
  293.        
  294.     spinlock_unlock(&irq_conns[mq].lock);
  295. }
  296.  
  297.  
  298. /** Initialize table of interrupt handlers
  299.  *
  300.  * @param irqcount Count of required hardware IRQs to be supported
  301.  */
  302. void ipc_irq_make_table(int irqcount)
  303. {
  304.     int i;
  305.  
  306.     irqcount +=  IPC_IRQ_RESERVED_VIRTUAL;
  307.  
  308.     irq_conns_size = irqcount;
  309.     irq_conns = malloc(irqcount * (sizeof(*irq_conns)), 0);
  310.     for (i=0; i < irqcount; i++) {
  311.         spinlock_initialize(&irq_conns[i].lock, "irq_ipc_lock");
  312.         irq_conns[i].box = NULL;
  313.         irq_conns[i].code = NULL;
  314.     }
  315. }
  316.  
  317. /** Disconnect all irq's notifications
  318.  *
  319.  * @todo It may be better to do some linked list, so that
  320.  *       we wouldn't need to go through whole array every cleanup
  321.  */
  322. void ipc_irq_cleanup(answerbox_t *box)
  323. {
  324.     int i;
  325.     ipl_t ipl;
  326.    
  327.     for (i=0; i < irq_conns_size; i++) {
  328.         ipl = interrupts_disable();
  329.         spinlock_lock(&irq_conns[i].lock);
  330.         if (irq_conns[i].box == box)
  331.             irq_conns[i].box = NULL;
  332.         spinlock_unlock(&irq_conns[i].lock);
  333.         interrupts_restore(ipl);
  334.     }
  335. }
  336.  
  337. /** @}
  338.  */
  339.