Subversion Repositories HelenOS

Rev

Rev 1281 | Rev 1288 | 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. /** IRQ notification framework
  30.  *
  31.  * This framework allows applications to register to receive a notification
  32.  * when interrupt is detected. The application may provide a simple 'top-half'
  33.  * handler as part of its registration, which can perform simple operations
  34.  * (read/write port/memory, add information to notification ipc message).
  35.  *
  36.  * The structure of a notification message is as follows:
  37.  * - METHOD: IPC_M_INTERRUPT
  38.  * - ARG1: interrupt number
  39.  * - ARG2: payload modified by a 'top-half' handler
  40.  * - ARG3: interrupt counter (may be needed to assure correct order
  41.  *         in multithreaded drivers)
  42.  */
  43.  
  44. #include <arch.h>
  45. #include <mm/slab.h>
  46. #include <errno.h>
  47. #include <ipc/ipc.h>
  48. #include <ipc/irq.h>
  49. #include <atomic.h>
  50.  
  51. typedef struct {
  52.     SPINLOCK_DECLARE(lock);
  53.     answerbox_t *box;
  54.     irq_code_t *code;
  55.     atomic_t counter;
  56. } ipc_irq_t;
  57.  
  58.  
  59. static ipc_irq_t *irq_conns = NULL;
  60. static int irq_conns_size;
  61.  
  62. #include <print.h>
  63. /* Execute code associated with IRQ notification */
  64. static void code_execute(call_t *call, irq_code_t *code)
  65. {
  66.     int i;
  67.  
  68.     if (!code)
  69.         return;
  70.    
  71.     for (i=0; i < code->cmdcount;i++) {
  72.         switch (code->cmds[i].cmd) {
  73.         case CMD_MEM_READ_1:
  74.             IPC_SET_ARG2(call->data, *((__u8 *)code->cmds[i].addr));
  75.             break;
  76.         case CMD_MEM_READ_2:
  77.             IPC_SET_ARG2(call->data, *((__u16 *)code->cmds[i].addr));
  78.             break;
  79.         case CMD_MEM_READ_4:
  80.             IPC_SET_ARG2(call->data, *((__u32 *)code->cmds[i].addr));
  81.             break;
  82.         case CMD_MEM_READ_8:
  83.             IPC_SET_ARG2(call->data, *((__u64 *)code->cmds[i].addr));
  84.             break;
  85.         case CMD_MEM_WRITE_1:
  86.             *((__u8 *)code->cmds[i].addr) = code->cmds[i].value;
  87.             break;
  88.         case CMD_MEM_WRITE_2:
  89.             *((__u16 *)code->cmds[i].addr) = code->cmds[i].value;
  90.             break;
  91.         case CMD_MEM_WRITE_4:
  92.             *((__u32 *)code->cmds[i].addr) = code->cmds[i].value;
  93.             break;
  94.         case CMD_MEM_WRITE_8:
  95.             *((__u64 *)code->cmds[i].addr) = code->cmds[i].value;
  96.             break;
  97. #if defined(ia32) || defined(amd64)
  98.         case CMD_PORT_READ_1:
  99.             IPC_SET_ARG2(call->data, inb((long)code->cmds[i].addr));
  100.             break;
  101.         case CMD_PORT_WRITE_1:
  102.             outb((long)code->cmds[i].addr, code->cmds[i].value);
  103.             break;
  104. #endif
  105.         default:
  106.             break;
  107.         }
  108.     }
  109. }
  110.  
  111. static void code_free(irq_code_t *code)
  112. {
  113.     if (code) {
  114.         free(code->cmds);
  115.         free(code);
  116.     }
  117. }
  118.  
  119. static irq_code_t * code_from_uspace(irq_code_t *ucode)
  120. {
  121.     irq_code_t *code;
  122.     irq_cmd_t *ucmds;
  123.  
  124.     code = malloc(sizeof(*code), 0);
  125.     copy_from_uspace(code, ucode, sizeof(*code));
  126.    
  127.     if (code->cmdcount > IRQ_MAX_PROG_SIZE) {
  128.         free(code);
  129.         return NULL;
  130.     }
  131.     ucmds = code->cmds;
  132.     code->cmds = malloc(sizeof(code->cmds[0]) * (code->cmdcount), 0);
  133.     copy_from_uspace(code->cmds, ucmds, sizeof(code->cmds[0]) * (code->cmdcount));
  134.  
  135.     return code;
  136. }
  137.  
  138. /** Unregister task from irq */
  139. void ipc_irq_unregister(answerbox_t *box, int irq)
  140. {
  141.     ipl_t ipl;
  142.  
  143.     ipl = interrupts_disable();
  144.     spinlock_lock(&irq_conns[irq].lock);
  145.     if (irq_conns[irq].box == box) {
  146.         irq_conns[irq].box = NULL;
  147.         code_free(irq_conns[irq].code);
  148.         irq_conns[irq].code = NULL;
  149.     }
  150.  
  151.     spinlock_unlock(&irq_conns[irq].lock);
  152.     interrupts_restore(ipl);
  153. }
  154.  
  155. /** Register an answerbox as a receiving end of interrupts notifications */
  156. int ipc_irq_register(answerbox_t *box, int irq, irq_code_t *ucode)
  157. {
  158.     ipl_t ipl;
  159.     irq_code_t *code;
  160.  
  161.     ASSERT(irq_conns);
  162.  
  163.     if (ucode) {
  164.         code = code_from_uspace(ucode);
  165.         if (!code)
  166.             return EBADMEM;
  167.     } else
  168.         code = NULL;
  169.  
  170.     ipl = interrupts_disable();
  171.     spinlock_lock(&irq_conns[irq].lock);
  172.  
  173.     if (irq_conns[irq].box) {
  174.         spinlock_unlock(&irq_conns[irq].lock);
  175.         interrupts_restore(ipl);
  176.         code_free(code);
  177.         return EEXISTS;
  178.     }
  179.     irq_conns[irq].box = box;
  180.     irq_conns[irq].code = code;
  181.     atomic_set(&irq_conns[irq].counter, 0);
  182.     spinlock_unlock(&irq_conns[irq].lock);
  183.     interrupts_restore(ipl);
  184.  
  185.     return 0;
  186. }
  187.  
  188. /** Notify process that an irq had happend
  189.  *
  190.  * We expect interrupts to be disabled
  191.  */
  192. void ipc_irq_send_notif(int irq)
  193. {
  194.     call_t *call;
  195.  
  196.     ASSERT(irq_conns);
  197.     spinlock_lock(&irq_conns[irq].lock);
  198.  
  199.     if (irq_conns[irq].box) {
  200.         call = ipc_call_alloc(FRAME_ATOMIC);
  201.         call->flags |= IPC_CALL_NOTIF;
  202.         IPC_SET_METHOD(call->data, IPC_M_INTERRUPT);
  203.         IPC_SET_ARG1(call->data, irq);
  204.         IPC_SET_ARG3(call->data, atomic_preinc(&irq_conns[irq].counter));
  205.  
  206.         /* Execute code to handle irq */
  207.         code_execute(call, irq_conns[irq].code);
  208.  
  209.         spinlock_lock(&irq_conns[irq].box->irq_lock);
  210.         list_append(&call->list, &irq_conns[irq].box->irq_notifs);
  211.         spinlock_unlock(&irq_conns[irq].box->irq_lock);
  212.  
  213.         waitq_wakeup(&irq_conns[irq].box->wq, 0);
  214.     }
  215.        
  216.     spinlock_unlock(&irq_conns[irq].lock);
  217. }
  218.  
  219.  
  220. /** Initialize table of interrupt handlers */
  221. void ipc_irq_make_table(int irqcount)
  222. {
  223.     int i;
  224.  
  225.     irq_conns_size = irqcount;
  226.     irq_conns = malloc(irqcount * (sizeof(*irq_conns)), 0);
  227.     for (i=0; i < irqcount; i++) {
  228.         spinlock_initialize(&irq_conns[i].lock, "irq_ipc_lock");
  229.         irq_conns[i].box = NULL;
  230.         irq_conns[i].code = NULL;
  231.     }
  232. }
  233.  
  234. /** Disconnect all irq's notifications
  235.  *
  236.  * TODO: It may be better to do some linked list, so that
  237.  *       we wouldn't need to go through whole array every cleanup
  238.  */
  239. void ipc_irq_cleanup(answerbox_t *box)
  240. {
  241.     int i;
  242.     ipl_t ipl;
  243.    
  244.     for (i=0; i < irq_conns_size; i++) {
  245.         ipl = interrupts_disable();
  246.         spinlock_lock(&irq_conns[i].lock);
  247.         if (irq_conns[i].box == box)
  248.             irq_conns[i].box = NULL;
  249.         spinlock_unlock(&irq_conns[i].lock);
  250.         interrupts_restore(ipl);
  251.     }
  252. }
  253.