Subversion Repositories HelenOS

Rev

Rev 1573 | Rev 1595 | 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. #include <syscall/copy.h>
  51. #include <console/console.h>
  52.  
  53. typedef struct {
  54.     SPINLOCK_DECLARE(lock);
  55.     answerbox_t *box;
  56.     irq_code_t *code;
  57.     atomic_t counter;
  58. } ipc_irq_t;
  59.  
  60.  
  61. static ipc_irq_t *irq_conns = NULL;
  62. static int irq_conns_size;
  63.  
  64. #include <print.h>
  65. /* Execute code associated with IRQ notification */
  66. static void code_execute(call_t *call, irq_code_t *code)
  67. {
  68.     int i;
  69.  
  70.     if (!code)
  71.         return;
  72.    
  73.     for (i=0; i < code->cmdcount;i++) {
  74.         switch (code->cmds[i].cmd) {
  75.         case CMD_MEM_READ_1:
  76.             IPC_SET_ARG2(call->data, *((__u8 *)code->cmds[i].addr));
  77.             break;
  78.         case CMD_MEM_READ_2:
  79.             IPC_SET_ARG2(call->data, *((__u16 *)code->cmds[i].addr));
  80.             break;
  81.         case CMD_MEM_READ_4:
  82.             IPC_SET_ARG2(call->data, *((__u32 *)code->cmds[i].addr));
  83.             break;
  84.         case CMD_MEM_READ_8:
  85.             IPC_SET_ARG2(call->data, *((__u64 *)code->cmds[i].addr));
  86.             break;
  87.         case CMD_MEM_WRITE_1:
  88.             *((__u8 *)code->cmds[i].addr) = code->cmds[i].value;
  89.             break;
  90.         case CMD_MEM_WRITE_2:
  91.             *((__u16 *)code->cmds[i].addr) = code->cmds[i].value;
  92.             break;
  93.         case CMD_MEM_WRITE_4:
  94.             *((__u32 *)code->cmds[i].addr) = code->cmds[i].value;
  95.             break;
  96.         case CMD_MEM_WRITE_8:
  97.             *((__u64 *)code->cmds[i].addr) = code->cmds[i].value;
  98.             break;
  99. #if defined(ia32) || defined(amd64)
  100.         case CMD_PORT_READ_1:
  101.             IPC_SET_ARG2(call->data, inb((long)code->cmds[i].addr));
  102.             break;
  103.         case CMD_PORT_WRITE_1:
  104.             outb((long)code->cmds[i].addr, code->cmds[i].value);
  105.             break;
  106. #endif
  107. #if defined(ia64)
  108.         case CMD_IA64_GETCHAR:
  109.             IPC_SET_ARG2(call->data, _getc(&ski_uconsole));
  110.             break;
  111. #endif
  112.         default:
  113.             break;
  114.         }
  115.     }
  116. }
  117.  
  118. static void code_free(irq_code_t *code)
  119. {
  120.     if (code) {
  121.         free(code->cmds);
  122.         free(code);
  123.     }
  124. }
  125.  
  126. static irq_code_t * code_from_uspace(irq_code_t *ucode)
  127. {
  128.     irq_code_t *code;
  129.     irq_cmd_t *ucmds;
  130.     int rc;
  131.  
  132.     code = malloc(sizeof(*code), 0);
  133.     rc = copy_from_uspace(code, ucode, sizeof(*code));
  134.     if (rc != 0) {
  135.         free(code);
  136.         return NULL;
  137.     }
  138.    
  139.     if (code->cmdcount > IRQ_MAX_PROG_SIZE) {
  140.         free(code);
  141.         return NULL;
  142.     }
  143.     ucmds = code->cmds;
  144.     code->cmds = malloc(sizeof(code->cmds[0]) * (code->cmdcount), 0);
  145.     rc = copy_from_uspace(code->cmds, ucmds, sizeof(code->cmds[0]) * (code->cmdcount));
  146.     if (rc != 0) {
  147.         free(code->cmds);
  148.         free(code);
  149.         return NULL;
  150.     }
  151.  
  152.     return code;
  153. }
  154.  
  155. /** Unregister task from irq */
  156. void ipc_irq_unregister(answerbox_t *box, int irq)
  157. {
  158.     ipl_t ipl;
  159.  
  160.     ipl = interrupts_disable();
  161.     spinlock_lock(&irq_conns[irq].lock);
  162.     if (irq_conns[irq].box == box) {
  163.         irq_conns[irq].box = NULL;
  164.         code_free(irq_conns[irq].code);
  165.         irq_conns[irq].code = NULL;
  166.     }
  167.  
  168.     spinlock_unlock(&irq_conns[irq].lock);
  169.     interrupts_restore(ipl);
  170. }
  171.  
  172. /** Register an answerbox as a receiving end of interrupts notifications */
  173. int ipc_irq_register(answerbox_t *box, int irq, irq_code_t *ucode)
  174. {
  175.     ipl_t ipl;
  176.     irq_code_t *code;
  177.  
  178.     ASSERT(irq_conns);
  179.  
  180.     if (ucode) {
  181.         code = code_from_uspace(ucode);
  182.         if (!code)
  183.             return EBADMEM;
  184.     } else
  185.         code = NULL;
  186.  
  187.     ipl = interrupts_disable();
  188.     spinlock_lock(&irq_conns[irq].lock);
  189.  
  190.     if (irq_conns[irq].box) {
  191.         spinlock_unlock(&irq_conns[irq].lock);
  192.         interrupts_restore(ipl);
  193.         code_free(code);
  194.         return EEXISTS;
  195.     }
  196.     irq_conns[irq].box = box;
  197.     irq_conns[irq].code = code;
  198.     atomic_set(&irq_conns[irq].counter, 0);
  199.     spinlock_unlock(&irq_conns[irq].lock);
  200.     interrupts_restore(ipl);
  201.  
  202.     return 0;
  203. }
  204.  
  205. /** Notify process that an irq had happend
  206.  *
  207.  * We expect interrupts to be disabled
  208.  */
  209. void ipc_irq_send_notif(int irq)
  210. {
  211.     call_t *call;
  212.  
  213.     ASSERT(irq_conns);
  214.     spinlock_lock(&irq_conns[irq].lock);
  215.  
  216.     if (irq_conns[irq].box) {
  217.         call = ipc_call_alloc(FRAME_ATOMIC);
  218.         if (!call) {
  219.             spinlock_unlock(&irq_conns[irq].lock);
  220.             return;
  221.         }
  222.         call->flags |= IPC_CALL_NOTIF;
  223.         IPC_SET_METHOD(call->data, IPC_M_INTERRUPT);
  224.         IPC_SET_ARG1(call->data, irq);
  225.         IPC_SET_ARG3(call->data, atomic_preinc(&irq_conns[irq].counter));
  226.  
  227.         /* Execute code to handle irq */
  228.         code_execute(call, irq_conns[irq].code);
  229.  
  230.         spinlock_lock(&irq_conns[irq].box->irq_lock);
  231.         list_append(&call->link, &irq_conns[irq].box->irq_notifs);
  232.         spinlock_unlock(&irq_conns[irq].box->irq_lock);
  233.  
  234.         waitq_wakeup(&irq_conns[irq].box->wq, 0);
  235.     }
  236.        
  237.     spinlock_unlock(&irq_conns[irq].lock);
  238. }
  239.  
  240.  
  241. /** Initialize table of interrupt handlers */
  242. void ipc_irq_make_table(int irqcount)
  243. {
  244.     int i;
  245.  
  246.     irq_conns_size = irqcount;
  247.     irq_conns = malloc(irqcount * (sizeof(*irq_conns)), 0);
  248.     for (i=0; i < irqcount; i++) {
  249.         spinlock_initialize(&irq_conns[i].lock, "irq_ipc_lock");
  250.         irq_conns[i].box = NULL;
  251.         irq_conns[i].code = NULL;
  252.     }
  253. }
  254.  
  255. /** Disconnect all irq's notifications
  256.  *
  257.  * TODO: It may be better to do some linked list, so that
  258.  *       we wouldn't need to go through whole array every cleanup
  259.  */
  260. void ipc_irq_cleanup(answerbox_t *box)
  261. {
  262.     int i;
  263.     ipl_t ipl;
  264.    
  265.     for (i=0; i < irq_conns_size; i++) {
  266.         ipl = interrupts_disable();
  267.         spinlock_lock(&irq_conns[i].lock);
  268.         if (irq_conns[i].box == box)
  269.             irq_conns[i].box = NULL;
  270.         spinlock_unlock(&irq_conns[i].lock);
  271.         interrupts_restore(ipl);
  272.     }
  273. }
  274.