Subversion Repositories HelenOS-historic

Rev

Rev 1591 | Rev 1625 | 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.     int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
  160.  
  161.     ipl = interrupts_disable();
  162.     spinlock_lock(&irq_conns[mq].lock);
  163.     if (irq_conns[mq].box == box) {
  164.         irq_conns[mq].box = NULL;
  165.         code_free(irq_conns[mq].code);
  166.         irq_conns[mq].code = NULL;
  167.     }
  168.  
  169.     spinlock_unlock(&irq_conns[mq].lock);
  170.     interrupts_restore(ipl);
  171. }
  172.  
  173. /** Register an answerbox as a receiving end of interrupts notifications */
  174. int ipc_irq_register(answerbox_t *box, int irq, irq_code_t *ucode)
  175. {
  176.     ipl_t ipl;
  177.     irq_code_t *code;
  178.     int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
  179.  
  180.     ASSERT(irq_conns);
  181.  
  182.     if (ucode) {
  183.         code = code_from_uspace(ucode);
  184.         if (!code)
  185.             return EBADMEM;
  186.     } else
  187.         code = NULL;
  188.  
  189.     ipl = interrupts_disable();
  190.     spinlock_lock(&irq_conns[mq].lock);
  191.  
  192.     if (irq_conns[mq].box) {
  193.         spinlock_unlock(&irq_conns[mq].lock);
  194.         interrupts_restore(ipl);
  195.         code_free(code);
  196.         return EEXISTS;
  197.     }
  198.     irq_conns[mq].box = box;
  199.     irq_conns[mq].code = code;
  200.     atomic_set(&irq_conns[mq].counter, 0);
  201.     spinlock_unlock(&irq_conns[mq].lock);
  202.     interrupts_restore(ipl);
  203.  
  204.     return 0;
  205. }
  206.  
  207. /** Add call to proper answerbox queue
  208.  *
  209.  * Assume irq_conns[mq].lock is locked */
  210. static void send_call(int mq, call_t *call)
  211. {
  212.     spinlock_lock(&irq_conns[mq].box->irq_lock);
  213.     list_append(&call->link, &irq_conns[mq].box->irq_notifs);
  214.     spinlock_unlock(&irq_conns[mq].box->irq_lock);
  215.        
  216.     waitq_wakeup(&irq_conns[mq].box->wq, 0);
  217. }
  218.  
  219. /** Send notification message
  220.  *
  221.  */
  222. void ipc_irq_send_msg(int irq, __native a2, __native a3)
  223. {
  224.     call_t *call;
  225.     int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
  226.  
  227.     spinlock_lock(&irq_conns[mq].lock);
  228.  
  229.     if (irq_conns[mq].box) {
  230.         call = ipc_call_alloc(FRAME_ATOMIC);
  231.         if (!call) {
  232.             spinlock_unlock(&irq_conns[mq].lock);
  233.             return;
  234.         }
  235.         call->flags |= IPC_CALL_NOTIF;
  236.         IPC_SET_METHOD(call->data, IPC_M_INTERRUPT);
  237.         IPC_SET_ARG1(call->data, irq);
  238.         IPC_SET_ARG2(call->data, a2);
  239.         IPC_SET_ARG3(call->data, a3);
  240.        
  241.         send_call(mq, call);
  242.     }
  243.     spinlock_unlock(&irq_conns[mq].lock);
  244. }
  245.  
  246. /** Notify process that an irq had happend
  247.  *
  248.  * We expect interrupts to be disabled
  249.  */
  250. void ipc_irq_send_notif(int irq)
  251. {
  252.     call_t *call;
  253.     int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
  254.  
  255.     ASSERT(irq_conns);
  256.     spinlock_lock(&irq_conns[mq].lock);
  257.  
  258.     if (irq_conns[mq].box) {
  259.         call = ipc_call_alloc(FRAME_ATOMIC);
  260.         if (!call) {
  261.             spinlock_unlock(&irq_conns[mq].lock);
  262.             return;
  263.         }
  264.         call->flags |= IPC_CALL_NOTIF;
  265.         IPC_SET_METHOD(call->data, IPC_M_INTERRUPT);
  266.         IPC_SET_ARG1(call->data, irq);
  267.         IPC_SET_ARG3(call->data, atomic_preinc(&irq_conns[mq].counter));
  268.  
  269.         /* Execute code to handle irq */
  270.         code_execute(call, irq_conns[mq].code);
  271.        
  272.         send_call(mq, call);
  273.     }
  274.        
  275.     spinlock_unlock(&irq_conns[mq].lock);
  276. }
  277.  
  278.  
  279. /** Initialize table of interrupt handlers
  280.  *
  281.  * @param irqcount Count of required hardware IRQs to be supported
  282.  */
  283. void ipc_irq_make_table(int irqcount)
  284. {
  285.     int i;
  286.  
  287.     irqcount +=  IPC_IRQ_RESERVED_VIRTUAL;
  288.  
  289.     irq_conns_size = irqcount;
  290.     irq_conns = malloc(irqcount * (sizeof(*irq_conns)), 0);
  291.     for (i=0; i < irqcount; i++) {
  292.         spinlock_initialize(&irq_conns[i].lock, "irq_ipc_lock");
  293.         irq_conns[i].box = NULL;
  294.         irq_conns[i].code = NULL;
  295.     }
  296. }
  297.  
  298. /** Disconnect all irq's notifications
  299.  *
  300.  * TODO: It may be better to do some linked list, so that
  301.  *       we wouldn't need to go through whole array every cleanup
  302.  */
  303. void ipc_irq_cleanup(answerbox_t *box)
  304. {
  305.     int i;
  306.     ipl_t ipl;
  307.    
  308.     for (i=0; i < irq_conns_size; i++) {
  309.         ipl = interrupts_disable();
  310.         spinlock_lock(&irq_conns[i].lock);
  311.         if (irq_conns[i].box == box)
  312.             irq_conns[i].box = NULL;
  313.         spinlock_unlock(&irq_conns[i].lock);
  314.         interrupts_restore(ipl);
  315.     }
  316. }
  317.