Subversion Repositories HelenOS

Rev

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