Subversion Repositories HelenOS

Rev

Rev 2183 | Rev 2626 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (c) 2006 Ondrej Palkovsky
  3.  * Copyright (c) 2006 Jakub Jermar
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms, with or without
  7.  * modification, are permitted provided that the following conditions
  8.  * are met:
  9.  *
  10.  * - Redistributions of source code must retain the above copyright
  11.  *   notice, this list of conditions and the following disclaimer.
  12.  * - Redistributions in binary form must reproduce the above copyright
  13.  *   notice, this list of conditions and the following disclaimer in the
  14.  *   documentation and/or other materials provided with the distribution.
  15.  * - The name of the author may not be used to endorse or promote products
  16.  *   derived from this software without specific prior written permission.
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  19.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  20.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  21.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  22.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  23.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  24.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  25.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28.  */
  29.  
  30. /** @addtogroup genericipc
  31.  * @{
  32.  */
  33. /**
  34.  * @file
  35.  * @brief IRQ notification framework.
  36.  *
  37.  * This framework allows applications to register to receive a notification
  38.  * when interrupt is detected. The application may provide a simple 'top-half'
  39.  * handler as part of its registration, which can perform simple operations
  40.  * (read/write port/memory, add information to notification ipc message).
  41.  *
  42.  * The structure of a notification message is as follows:
  43.  * - METHOD: method as registered by the SYS_IPC_REGISTER_IRQ syscall
  44.  * - ARG1: payload modified by a 'top-half' handler
  45.  * - ARG2: payload modified by a 'top-half' handler
  46.  * - ARG3: payload modified by a 'top-half' handler
  47.  * - in_phone_hash: interrupt counter (may be needed to assure correct order
  48.  *         in multithreaded drivers)
  49.  */
  50.  
  51. #include <arch.h>
  52. #include <mm/slab.h>
  53. #include <errno.h>
  54. #include <ddi/irq.h>
  55. #include <ipc/ipc.h>
  56. #include <ipc/irq.h>
  57. #include <syscall/copy.h>
  58. #include <console/console.h>
  59. #include <print.h>
  60.  
  61. /** Execute code associated with IRQ notification.
  62.  *
  63.  * @param call      Notification call.
  64.  * @param code      Top-half pseudocode.
  65.  */
  66. static void code_execute(call_t *call, irq_code_t *code)
  67. {
  68.     int i;
  69.     unative_t dstval = 0;
  70.    
  71.     if (!code)
  72.         return;
  73.    
  74.     for (i = 0; i < code->cmdcount; i++) {
  75.         switch (code->cmds[i].cmd) {
  76.         case CMD_MEM_READ_1:
  77.             dstval = *((uint8_t *) code->cmds[i].addr);
  78.             break;
  79.         case CMD_MEM_READ_2:
  80.             dstval = *((uint16_t *) code->cmds[i].addr);
  81.             break;
  82.         case CMD_MEM_READ_4:
  83.             dstval = *((uint32_t *) code->cmds[i].addr);
  84.             break;
  85.         case CMD_MEM_READ_8:
  86.             dstval = *((uint64_t *) code->cmds[i].addr);
  87.             break;
  88.         case CMD_MEM_WRITE_1:
  89.             *((uint8_t *) code->cmds[i].addr) = code->cmds[i].value;
  90.             break;
  91.         case CMD_MEM_WRITE_2:
  92.             *((uint16_t *) code->cmds[i].addr) = code->cmds[i].value;
  93.             break;
  94.         case CMD_MEM_WRITE_4:
  95.             *((uint32_t *) code->cmds[i].addr) = code->cmds[i].value;
  96.             break;
  97.         case CMD_MEM_WRITE_8:
  98.             *((uint64_t *) code->cmds[i].addr) = code->cmds[i].value;
  99.             break;
  100. #if defined(ia32) || defined(amd64)
  101.         case CMD_PORT_READ_1:
  102.             dstval = inb((long) code->cmds[i].addr);
  103.             break;
  104.         case CMD_PORT_WRITE_1:
  105.             outb((long) code->cmds[i].addr, code->cmds[i].value);
  106.             break;
  107. #endif
  108. #if defined(ia64) && defined(SKI)
  109.         case CMD_IA64_GETCHAR:
  110.             dstval = _getc(&ski_uconsole);
  111.             break;
  112. #endif
  113. #if defined(ppc32)
  114.         case CMD_PPC32_GETCHAR:
  115.             dstval = cuda_get_scancode();
  116.             break;
  117. #endif
  118.         default:
  119.             break;
  120.         }
  121.         if (code->cmds[i].dstarg && code->cmds[i].dstarg < 4) {
  122.             call->data.args[code->cmds[i].dstarg] = dstval;
  123.         }
  124.     }
  125. }
  126.  
  127. /** Free top-half pseudocode.
  128.  *
  129.  * @param code      Pointer to the top-half pseudocode.
  130.  */
  131. static void code_free(irq_code_t *code)
  132. {
  133.     if (code) {
  134.         free(code->cmds);
  135.         free(code);
  136.     }
  137. }
  138.  
  139. /** Copy top-half pseudocode from userspace into the kernel.
  140.  *
  141.  * @param ucode     Userspace address of the top-half pseudocode.
  142.  *
  143.  * @return      Kernel address of the copied pseudocode.
  144.  */
  145. static irq_code_t *code_from_uspace(irq_code_t *ucode)
  146. {
  147.     irq_code_t *code;
  148.     irq_cmd_t *ucmds;
  149.     int rc;
  150.  
  151.     code = malloc(sizeof(*code), 0);
  152.     rc = copy_from_uspace(code, ucode, sizeof(*code));
  153.     if (rc != 0) {
  154.         free(code);
  155.         return NULL;
  156.     }
  157.    
  158.     if (code->cmdcount > IRQ_MAX_PROG_SIZE) {
  159.         free(code);
  160.         return NULL;
  161.     }
  162.     ucmds = code->cmds;
  163.     code->cmds = malloc(sizeof(code->cmds[0]) * code->cmdcount, 0);
  164.     rc = copy_from_uspace(code->cmds, ucmds,
  165.         sizeof(code->cmds[0]) * code->cmdcount);
  166.     if (rc != 0) {
  167.         free(code->cmds);
  168.         free(code);
  169.         return NULL;
  170.     }
  171.  
  172.     return code;
  173. }
  174.  
  175. /** Unregister task from IRQ notification.
  176.  *
  177.  * @param box       Answerbox associated with the notification.
  178.  * @param inr       IRQ number.
  179.  * @param devno     Device number.
  180.  */
  181. void ipc_irq_unregister(answerbox_t *box, inr_t inr, devno_t devno)
  182. {
  183.     ipl_t ipl;
  184.     irq_t *irq;
  185.  
  186.     ipl = interrupts_disable();
  187.     irq = irq_find_and_lock(inr, devno);
  188.     if (irq) {
  189.         if (irq->notif_cfg.answerbox == box) {
  190.             code_free(irq->notif_cfg.code);
  191.             irq->notif_cfg.notify = false;
  192.             irq->notif_cfg.answerbox = NULL;
  193.             irq->notif_cfg.code = NULL;
  194.             irq->notif_cfg.method = 0;
  195.             irq->notif_cfg.counter = 0;
  196.  
  197.             spinlock_lock(&box->irq_lock);
  198.             list_remove(&irq->notif_cfg.link);
  199.             spinlock_unlock(&box->irq_lock);
  200.            
  201.             spinlock_unlock(&irq->lock);
  202.         }
  203.     }
  204.     interrupts_restore(ipl);
  205. }
  206.  
  207. /** Register an answerbox as a receiving end for IRQ notifications.
  208.  *
  209.  * @param box       Receiving answerbox.
  210.  * @param inr       IRQ number.
  211.  * @param devno     Device number.
  212.  * @param method    Method to be associated with the notification.
  213.  * @param ucode     Uspace pointer to top-half pseudocode.
  214.  *
  215.  * @return      EBADMEM, ENOENT or EEXISTS on failure or 0 on success.
  216.  */
  217. int ipc_irq_register(answerbox_t *box, inr_t inr, devno_t devno,
  218.     unative_t method, irq_code_t *ucode)
  219. {
  220.     ipl_t ipl;
  221.     irq_code_t *code;
  222.     irq_t *irq;
  223.  
  224.     if (ucode) {
  225.         code = code_from_uspace(ucode);
  226.         if (!code)
  227.             return EBADMEM;
  228.     } else {
  229.         code = NULL;
  230.     }
  231.  
  232.     ipl = interrupts_disable();
  233.     irq = irq_find_and_lock(inr, devno);
  234.     if (!irq) {
  235.         interrupts_restore(ipl);
  236.         code_free(code);
  237.         return ENOENT;
  238.     }
  239.    
  240.     if (irq->notif_cfg.answerbox) {
  241.         spinlock_unlock(&irq->lock);
  242.         interrupts_restore(ipl);
  243.         code_free(code);
  244.         return EEXISTS;
  245.     }
  246.    
  247.     irq->notif_cfg.notify = true;
  248.     irq->notif_cfg.answerbox = box;
  249.     irq->notif_cfg.method = method;
  250.     irq->notif_cfg.code = code;
  251.     irq->notif_cfg.counter = 0;
  252.  
  253.     spinlock_lock(&box->irq_lock);
  254.     list_append(&irq->notif_cfg.link, &box->irq_head);
  255.     spinlock_unlock(&box->irq_lock);
  256.  
  257.     spinlock_unlock(&irq->lock);
  258.     interrupts_restore(ipl);
  259.  
  260.     return 0;
  261. }
  262.  
  263. /** Add a call to the proper answerbox queue.
  264.  *
  265.  * Assume irq->lock is locked.
  266.  *
  267.  * @param irq       IRQ structure referencing the target answerbox.
  268.  * @param call      IRQ notification call.
  269.  */
  270. static void send_call(irq_t *irq, call_t *call)
  271. {
  272.     spinlock_lock(&irq->notif_cfg.answerbox->irq_lock);
  273.     list_append(&call->link, &irq->notif_cfg.answerbox->irq_notifs);
  274.     spinlock_unlock(&irq->notif_cfg.answerbox->irq_lock);
  275.        
  276.     waitq_wakeup(&irq->notif_cfg.answerbox->wq, WAKEUP_FIRST);
  277. }
  278.  
  279. /** Send notification message.
  280.  *
  281.  * @param irq       IRQ structure.
  282.  * @param a1        Driver-specific payload argument.
  283.  * @param a2        Driver-specific payload argument.
  284.  * @param a3        Driver-specific payload argument.
  285.  */
  286. void ipc_irq_send_msg(irq_t *irq, unative_t a1, unative_t a2, unative_t a3)
  287. {
  288.     call_t *call;
  289.  
  290.     spinlock_lock(&irq->lock);
  291.  
  292.     if (irq->notif_cfg.answerbox) {
  293.         call = ipc_call_alloc(FRAME_ATOMIC);
  294.         if (!call) {
  295.             spinlock_unlock(&irq->lock);
  296.             return;
  297.         }
  298.         call->flags |= IPC_CALL_NOTIF;
  299.         IPC_SET_METHOD(call->data, irq->notif_cfg.method);
  300.         IPC_SET_ARG1(call->data, a1);
  301.         IPC_SET_ARG2(call->data, a2);
  302.         IPC_SET_ARG3(call->data, a3);
  303.         /* Put a counter to the message */
  304.         call->priv = ++irq->notif_cfg.counter;
  305.        
  306.         send_call(irq, call);
  307.     }
  308.     spinlock_unlock(&irq->lock);
  309. }
  310.  
  311. /** Notify a task that an IRQ had occurred.
  312.  *
  313.  * We expect interrupts to be disabled and the irq->lock already held.
  314.  *
  315.  * @param irq       IRQ structure.
  316.  */
  317. void ipc_irq_send_notif(irq_t *irq)
  318. {
  319.     call_t *call;
  320.  
  321.     ASSERT(irq);
  322.  
  323.     if (irq->notif_cfg.answerbox) {
  324.         call = ipc_call_alloc(FRAME_ATOMIC);
  325.         if (!call) {
  326.             return;
  327.         }
  328.         call->flags |= IPC_CALL_NOTIF;
  329.         /* Put a counter to the message */
  330.         call->priv = ++irq->notif_cfg.counter;
  331.         /* Set up args */
  332.         IPC_SET_METHOD(call->data, irq->notif_cfg.method);
  333.  
  334.         /* Execute code to handle irq */
  335.         code_execute(call, irq->notif_cfg.code);
  336.        
  337.         send_call(irq, call);
  338.     }
  339. }
  340.  
  341. /** Disconnect all IRQ notifications from an answerbox.
  342.  *
  343.  * This function is effective because the answerbox contains
  344.  * list of all irq_t structures that are registered to
  345.  * send notifications to it.
  346.  *
  347.  * @param box       Answerbox for which we want to carry out the cleanup.
  348.  */
  349. void ipc_irq_cleanup(answerbox_t *box)
  350. {
  351.     ipl_t ipl;
  352.    
  353. loop:
  354.     ipl = interrupts_disable();
  355.     spinlock_lock(&box->irq_lock);
  356.    
  357.     while (box->irq_head.next != &box->irq_head) {
  358.         link_t *cur = box->irq_head.next;
  359.         irq_t *irq;
  360.         DEADLOCK_PROBE_INIT(p_irqlock);
  361.        
  362.         irq = list_get_instance(cur, irq_t, notif_cfg.link);
  363.         if (!spinlock_trylock(&irq->lock)) {
  364.             /*
  365.              * Avoid deadlock by trying again.
  366.              */
  367.             spinlock_unlock(&box->irq_lock);
  368.             interrupts_restore(ipl);
  369.             DEADLOCK_PROBE(p_irqlock, DEADLOCK_THRESHOLD);
  370.             goto loop;
  371.         }
  372.        
  373.         ASSERT(irq->notif_cfg.answerbox == box);
  374.        
  375.         list_remove(&irq->notif_cfg.link);
  376.        
  377.         /*
  378.          * Don't forget to free any top-half pseudocode.
  379.          */
  380.         code_free(irq->notif_cfg.code);
  381.        
  382.         irq->notif_cfg.notify = false;
  383.         irq->notif_cfg.answerbox = NULL;
  384.         irq->notif_cfg.code = NULL;
  385.         irq->notif_cfg.method = 0;
  386.         irq->notif_cfg.counter = 0;
  387.  
  388.         spinlock_unlock(&irq->lock);
  389.     }
  390.    
  391.     spinlock_unlock(&box->irq_lock);
  392.     interrupts_restore(ipl);
  393. }
  394.  
  395. /** @}
  396.  */
  397.